|
20 | 20 | </div>
|
21 | 21 | </template>
|
22 | 22 |
|
23 |
| -<script lang="ts"> |
24 |
| -import { defineComponent, ref, nextTick, Ref } from "vue"; |
25 |
| -
|
26 |
| -function valueFormatterDefault(value: number): string { |
27 |
| - return value.toString(); |
28 |
| -} |
29 |
| -
|
30 |
| -function valueParserDefault(value: string): number { |
31 |
| - return parseInt(value, 10); |
32 |
| -} |
33 |
| -
|
| 23 | +<script lang="ts" setup generic="T extends string | number"> |
34 | 24 | /**
|
35 | 25 | * Provides a value display that can be clicked to edit.
|
36 | 26 | */
|
37 |
| -const ClickToEdit = defineComponent({ |
38 |
| - name: "ClickToEdit", |
39 |
| - emits: ["change", "update:modelValue"], |
40 |
| - props: { |
41 |
| - modelValue: { |
42 |
| - type: [String, Number], |
43 |
| - required: true, |
44 |
| - }, |
45 |
| - valueFormatter: { |
46 |
| - type: Function, |
47 |
| - default: valueFormatterDefault, |
48 |
| - }, |
49 |
| - valueParser: { |
50 |
| - type: Function, |
51 |
| - default: valueParserDefault, |
52 |
| - }, |
53 |
| - }, |
54 |
| - setup(props, { emit }) { |
55 |
| - const editor = ref<HTMLInputElement | undefined>(); |
56 |
| - const valueFormatter = ref(props.valueFormatter) as Ref<(value: number) => string>; |
57 |
| - const valueParser = ref(props.valueParser) as Ref<(value: string) => number>; |
| 27 | +import { ref, nextTick, Ref } from "vue"; |
58 | 28 |
|
59 |
| - const editing = ref(false); |
60 |
| - const valueDirty = ref(); |
61 |
| - const display: Ref<HTMLDivElement | undefined> = ref(); |
62 |
| - const editorWidth = ref(120); |
| 29 | +const props = withDefaults( |
| 30 | + defineProps<{ |
| 31 | + valueFormatter?: (value: number) => string; |
| 32 | + valueParser?: (value: string) => number; |
| 33 | + }>(), |
| 34 | + { |
| 35 | + valueFormatter: (value: number): string => value.toString(), |
| 36 | + valueParser: (value: string): number => parseInt(value, 10), |
| 37 | + } |
| 38 | +); |
63 | 39 |
|
64 |
| - async function activate() { |
65 |
| - if (display.value) { |
66 |
| - editorWidth.value = display.value.offsetWidth + 24; |
67 |
| - } |
68 |
| - console.info("modelValue", props.modelValue); |
69 |
| - if (typeof props.modelValue === "number") { |
70 |
| - valueDirty.value = valueFormatter.value(props.modelValue); |
71 |
| - } else { |
72 |
| - valueDirty.value = props.modelValue; |
73 |
| - } |
74 |
| - editing.value = true; |
75 |
| - await nextTick(); |
76 |
| - editor.value?.focus(); |
77 |
| - } |
| 40 | +const model = defineModel<T>(); |
78 | 41 |
|
79 |
| - function apply() { |
80 |
| - let outValue: string | number; |
81 |
| - if (typeof props.modelValue === "number") { |
82 |
| - outValue = valueParser.value(valueDirty.value); |
83 |
| - } else { |
84 |
| - outValue = valueDirty.value; |
85 |
| - } |
86 |
| - editing.value = false; |
87 |
| - emit("change", outValue); |
88 |
| - emit("update:modelValue", outValue); |
89 |
| - } |
| 42 | +const emit = defineEmits<{ |
| 43 | + change: [value: T]; |
| 44 | +}>(); |
90 | 45 |
|
91 |
| - function abort() { |
92 |
| - editing.value = false; |
93 |
| - } |
| 46 | +const editor = ref<HTMLInputElement | undefined>(); |
| 47 | +const valueFormatter = ref(props.valueFormatter) as Ref<(value: number) => string>; |
| 48 | +const valueParser = ref(props.valueParser) as Ref<(value: string) => number>; |
94 | 49 |
|
95 |
| - return { |
96 |
| - editor, |
97 |
| - display, |
98 |
| - editing, |
99 |
| - valueDirty, |
100 |
| - editorWidth, |
| 50 | +const editing = ref(false); |
| 51 | +const valueDirty = ref(""); |
| 52 | +const display: Ref<HTMLDivElement | undefined> = ref(); |
| 53 | +const editorWidth = ref(120); |
101 | 54 |
|
102 |
| - activate, |
103 |
| - apply, |
104 |
| - abort, |
105 |
| - }; |
106 |
| - }, |
107 |
| -}); |
| 55 | +async function activate() { |
| 56 | + if (display.value) { |
| 57 | + editorWidth.value = display.value.offsetWidth + 24; |
| 58 | + } |
| 59 | + if (typeof model.value === "number") { |
| 60 | + valueDirty.value = valueFormatter.value(model.value); |
| 61 | + } else { |
| 62 | + valueDirty.value = model.value ?? ""; |
| 63 | + } |
| 64 | + editing.value = true; |
| 65 | + await nextTick(); |
| 66 | + editor.value?.focus(); |
| 67 | +} |
| 68 | +
|
| 69 | +function apply() { |
| 70 | + let outValue: T; |
| 71 | + if (typeof model.value === "number") { |
| 72 | + outValue = valueParser.value(valueDirty.value) as T; |
| 73 | + } else { |
| 74 | + outValue = valueDirty.value as T; |
| 75 | + } |
| 76 | + editing.value = false; |
| 77 | + model.value = outValue; |
| 78 | + emit("change", outValue); |
| 79 | +} |
108 | 80 |
|
109 |
| -export default ClickToEdit; |
| 81 | +function abort() { |
| 82 | + editing.value = false; |
| 83 | +} |
110 | 84 | </script>
|
111 | 85 |
|
112 | 86 | <style lang="scss">
|
|
0 commit comments