@expcat/tigercat-vue 2.0.4 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +20 -10
  2. package/dist/chunk-26QCTL2E.mjs +180 -0
  3. package/dist/{chunk-GS4SV2HK.mjs → chunk-32X54QKW.mjs} +1 -1
  4. package/dist/chunk-3ONCXHE5.mjs +184 -0
  5. package/dist/{chunk-KOG3HKOF.mjs → chunk-6AMWX5K4.mjs} +1 -4
  6. package/dist/chunk-6GR3XC5V.mjs +281 -0
  7. package/dist/chunk-6GR5U4UB.mjs +225 -0
  8. package/dist/chunk-7DWWNFRP.mjs +302 -0
  9. package/dist/chunk-AECXFT7D.mjs +121 -0
  10. package/dist/chunk-AOLRZG4E.mjs +347 -0
  11. package/dist/chunk-AY7CGLMO.mjs +368 -0
  12. package/dist/chunk-BBNCP55O.mjs +156 -0
  13. package/dist/chunk-F3ZVVVYD.mjs +257 -0
  14. package/dist/{chunk-OTGGPXDT.mjs → chunk-IEAEB4TX.mjs} +255 -44
  15. package/dist/chunk-P6FMU4AT.mjs +54 -0
  16. package/dist/chunk-PFIV5MXD.mjs +48 -0
  17. package/dist/chunk-TBOXTAIA.mjs +94 -0
  18. package/dist/{chunk-H6FZBFRT.mjs → chunk-TPESRDUG.mjs} +6 -1
  19. package/dist/{chunk-2XCXK3NQ.mjs → chunk-U4B5QT72.mjs} +1 -1
  20. package/dist/chunk-UUWPSRNV.mjs +337 -0
  21. package/dist/chunk-UXJRH72H.mjs +834 -0
  22. package/dist/chunk-VFS5XAZ3.mjs +602 -0
  23. package/dist/{chunk-WC5MKASU.mjs → chunk-VN2TWPRC.mjs} +169 -55
  24. package/dist/components/ActivityFeed.d.mts +1 -1
  25. package/dist/components/ActivityFeed.mjs +1 -1
  26. package/dist/components/Alert.d.mts +2 -2
  27. package/dist/components/AreaChart.mjs +2 -2
  28. package/dist/components/AspectRatio.d.mts +44 -0
  29. package/dist/components/AspectRatio.mjs +8 -0
  30. package/dist/components/BarChart.mjs +2 -2
  31. package/dist/components/Carousel.d.mts +1 -1
  32. package/dist/components/Cascader.d.mts +34 -0
  33. package/dist/components/Cascader.mjs +1 -1
  34. package/dist/components/ChartLegend.d.mts +1 -1
  35. package/dist/components/ChatWindow.mjs +1 -1
  36. package/dist/components/CodeEditor.mjs +1 -1
  37. package/dist/components/ColorSwatch.d.mts +1 -1
  38. package/dist/components/ContextMenu.d.mts +234 -0
  39. package/dist/components/ContextMenu.mjs +17 -0
  40. package/dist/components/DataTableWithToolbar.d.mts +1 -1
  41. package/dist/components/DataTableWithToolbar.mjs +2 -2
  42. package/dist/components/DonutChart.mjs +3 -3
  43. package/dist/components/Dropdown.d.mts +1 -1
  44. package/dist/components/FloatButton.d.mts +2 -2
  45. package/dist/components/FunnelChart.d.mts +1 -1
  46. package/dist/components/FunnelChart.mjs +2 -2
  47. package/dist/components/Highlight.d.mts +146 -0
  48. package/dist/components/Highlight.mjs +8 -0
  49. package/dist/components/ImageCompare.d.mts +284 -0
  50. package/dist/components/ImageCompare.mjs +8 -0
  51. package/dist/components/Input.d.mts +1 -1
  52. package/dist/components/InputOTP.d.mts +261 -0
  53. package/dist/components/InputOTP.mjs +11 -0
  54. package/dist/components/Kbd.d.mts +118 -0
  55. package/dist/components/Kbd.mjs +8 -0
  56. package/dist/components/LineChart.mjs +2 -2
  57. package/dist/components/List.d.mts +1 -1
  58. package/dist/components/List.mjs +1 -1
  59. package/dist/components/LoadingBar.d.mts +8 -0
  60. package/dist/components/LoadingBar.mjs +114 -0
  61. package/dist/components/LoadingBarContainer.d.mts +78 -0
  62. package/dist/components/LoadingBarContainer.mjs +8 -0
  63. package/dist/components/LoadingBarRoot.d.mts +6 -0
  64. package/dist/components/LoadingBarRoot.mjs +6 -0
  65. package/dist/components/Marquee.d.mts +158 -0
  66. package/dist/components/Marquee.mjs +8 -0
  67. package/dist/components/MaskInput.d.mts +201 -0
  68. package/dist/components/MaskInput.mjs +11 -0
  69. package/dist/components/Masonry.d.mts +66 -0
  70. package/dist/components/Masonry.mjs +8 -0
  71. package/dist/components/Menu.d.mts +2 -2
  72. package/dist/components/Modal.d.mts +1 -1
  73. package/dist/components/NavigationMenu.d.mts +411 -0
  74. package/dist/components/NavigationMenu.mjs +25 -0
  75. package/dist/components/NotificationCenter.d.mts +1 -1
  76. package/dist/components/NotificationCenter.mjs +2 -2
  77. package/dist/components/PageHeader.d.mts +140 -0
  78. package/dist/components/PageHeader.mjs +11 -0
  79. package/dist/components/PieChart.mjs +2 -2
  80. package/dist/components/Popconfirm.d.mts +3 -3
  81. package/dist/components/Popover.d.mts +2 -2
  82. package/dist/components/RadarChart.mjs +2 -2
  83. package/dist/components/ScatterChart.mjs +2 -2
  84. package/dist/components/ScrollArea.d.mts +136 -0
  85. package/dist/components/ScrollArea.mjs +8 -0
  86. package/dist/components/SplitButton.d.mts +286 -0
  87. package/dist/components/SplitButton.mjs +12 -0
  88. package/dist/components/Spotlight.d.mts +1 -1
  89. package/dist/components/SunburstChart.mjs +2 -2
  90. package/dist/components/TagsInput.d.mts +214 -0
  91. package/dist/components/TagsInput.mjs +12 -0
  92. package/dist/components/Timeline.d.mts +1 -1
  93. package/dist/components/Tooltip.d.mts +2 -2
  94. package/dist/components/Tour.d.mts +3 -3
  95. package/dist/components/Tree.d.mts +1 -1
  96. package/dist/components/TreeMapChart.d.mts +1 -1
  97. package/dist/components/TreeMapChart.mjs +2 -2
  98. package/dist/components/TreeSelect.d.mts +51 -0
  99. package/dist/components/TreeSelect.mjs +2 -1
  100. package/dist/components/Upload.d.mts +1 -1
  101. package/dist/components/VirtualTable.d.mts +1 -1
  102. package/dist/components/Watermark.d.mts +1 -1
  103. package/dist/index.d.mts +17 -1
  104. package/dist/index.mjs +356 -0
  105. package/package.json +117 -2
  106. package/dist/{chunk-MAKOSWR6.mjs → chunk-37W7XTA6.mjs} +3 -3
  107. package/dist/{chunk-WUQMFKQ6.mjs → chunk-66OS5A4D.mjs} +3 -3
  108. package/dist/{chunk-NNC75I7F.mjs → chunk-CHEXI7MO.mjs} +3 -3
  109. package/dist/{chunk-KFJIZED5.mjs → chunk-EHGFW5GX.mjs} +3 -3
  110. package/dist/{chunk-ZGA66PSG.mjs → chunk-EZYOCPMM.mjs} +3 -3
  111. package/dist/{chunk-XGXZF27U.mjs → chunk-JHMT5Q6P.mjs} +3 -3
  112. package/dist/{chunk-NGAA73CV.mjs → chunk-VLW3UBIR.mjs} +3 -3
  113. package/dist/{chunk-OTHJIFDV.mjs → chunk-W5XFWVNV.mjs} +3 -3
  114. package/dist/{chunk-3KITFMBJ.mjs → chunk-ZOYHZF4N.mjs} +3 -3
@@ -0,0 +1,347 @@
1
+ import {
2
+ FORM_ITEM_CONTROL_INJECTION_KEY
3
+ } from "./chunk-P2LZ2MR5.mjs";
4
+ import {
5
+ Tag
6
+ } from "./chunk-342DH5CF.mjs";
7
+ import {
8
+ useTigerConfig
9
+ } from "./chunk-UOJOJBEU.mjs";
10
+
11
+ // src/components/TagsInput.ts
12
+ import { computed, defineComponent, h, inject, ref, watch } from "vue";
13
+ import {
14
+ addTags,
15
+ classNames,
16
+ coerceClassValue,
17
+ extractTagCandidates,
18
+ formatRemoveTagLabel,
19
+ getTagsInputClearButtonClasses,
20
+ getTagsInputContainerClasses,
21
+ getTagsInputErrorClasses,
22
+ getTagsInputHighlightClasses,
23
+ getTagsInputInnerInputClasses,
24
+ getTagsInputLabels,
25
+ injectShakeStyle,
26
+ mergeStyleValues,
27
+ removeTagAt,
28
+ SHAKE_CLASS,
29
+ splitTagInput
30
+ } from "@expcat/tigercat-core";
31
+ var tagsInputIdCounter = 0;
32
+ var TagsInput = defineComponent({
33
+ name: "TigerTagsInput",
34
+ inheritAttrs: false,
35
+ props: {
36
+ /**
37
+ * Tags (for v-model)
38
+ */
39
+ modelValue: { type: Array, default: void 0 },
40
+ /**
41
+ * Default tags (for uncontrolled mode)
42
+ */
43
+ defaultValue: { type: Array, default: () => [] },
44
+ /**
45
+ * Input size
46
+ * @default 'md'
47
+ */
48
+ size: { type: String, default: "md" },
49
+ /**
50
+ * Validation status
51
+ * @default 'default'
52
+ */
53
+ status: { type: String, default: "default" },
54
+ /** Error message to display */
55
+ errorMessage: String,
56
+ /** Placeholder for the inner text input */
57
+ placeholder: { type: String, default: "" },
58
+ /** Whether the same tag may be added more than once */
59
+ allowDuplicates: Boolean,
60
+ /** Maximum number of tags */
61
+ max: { type: Number, default: void 0 },
62
+ /**
63
+ * Characters that commit input while typing and split pasted text
64
+ * @default [',']
65
+ */
66
+ delimiters: { type: Array, default: () => [","] },
67
+ /** Commit the pending input as a tag on blur */
68
+ addOnBlur: Boolean,
69
+ /** Validate/transform a candidate tag before it is added */
70
+ beforeAdd: {
71
+ type: Function,
72
+ default: void 0
73
+ },
74
+ /** Whether to show a clear-all button */
75
+ clearable: Boolean,
76
+ /** Whether the input is disabled */
77
+ disabled: Boolean,
78
+ /** Whether the input is readonly */
79
+ readonly: Boolean,
80
+ /** Name for the hidden input carrying the joined value */
81
+ name: String,
82
+ /** Id attribute applied to the inner text input */
83
+ id: String,
84
+ /** Aria-label template for tag remove buttons; supports {tag} */
85
+ removeTagAriaLabel: String,
86
+ /**
87
+ * Internal shake trigger counter (used by FormItem)
88
+ * @internal
89
+ */
90
+ _shakeTrigger: { type: Number, default: void 0 },
91
+ /** Additional CSS classes */
92
+ className: String,
93
+ /** Inline styles */
94
+ style: { type: Object, default: void 0 }
95
+ },
96
+ emits: {
97
+ "update:modelValue": null,
98
+ add: null,
99
+ remove: null,
100
+ clear: null,
101
+ focus: null,
102
+ blur: null
103
+ },
104
+ setup(props, { emit, attrs }) {
105
+ injectShakeStyle();
106
+ const config = useTigerConfig();
107
+ const formItemControl = inject(
108
+ FORM_ITEM_CONTROL_INJECTION_KEY,
109
+ null
110
+ );
111
+ const effectiveStatus = computed(
112
+ () => props.status !== "default" ? props.status : formItemControl?.status.value ?? props.status
113
+ );
114
+ const effectiveErrorMessage = computed(
115
+ () => props.errorMessage ?? formItemControl?.errorMessage.value
116
+ );
117
+ const effectiveShakeTrigger = computed(
118
+ () => props._shakeTrigger ?? formItemControl?.shakeTrigger.value
119
+ );
120
+ const labels = computed(() => getTagsInputLabels(config.value.locale));
121
+ const instanceId = ++tagsInputIdCounter;
122
+ const errorMsgId = `tiger-tags-input-error-${instanceId}`;
123
+ const containerRef = ref(null);
124
+ const inputRef = ref(null);
125
+ const isControlled = computed(() => props.modelValue !== void 0);
126
+ const innerTags = ref([...props.defaultValue]);
127
+ const tags = computed(() => isControlled.value ? props.modelValue ?? [] : innerTags.value);
128
+ const inputText = ref("");
129
+ const highlightedIndex = ref(null);
130
+ watch([effectiveStatus, effectiveShakeTrigger], ([newStatus]) => {
131
+ if (newStatus === "error" && containerRef.value) {
132
+ const el = containerRef.value;
133
+ el.classList.remove(SHAKE_CLASS);
134
+ void el.offsetWidth;
135
+ el.classList.add(SHAKE_CLASS);
136
+ }
137
+ });
138
+ const isInteractive = computed(() => !props.disabled && !props.readonly);
139
+ const setTags = (next) => {
140
+ if (!isControlled.value) innerTags.value = next;
141
+ emit("update:modelValue", next);
142
+ };
143
+ const commitCandidates = (candidates) => {
144
+ const prepared = [];
145
+ for (const raw of candidates) {
146
+ if (!props.beforeAdd) {
147
+ prepared.push(raw);
148
+ continue;
149
+ }
150
+ const result = props.beforeAdd(raw.trim());
151
+ if (result === false) continue;
152
+ prepared.push(typeof result === "string" ? result : raw);
153
+ }
154
+ if (prepared.length === 0) return [];
155
+ const { tags: nextTags, added } = addTags(tags.value, prepared, {
156
+ allowDuplicates: props.allowDuplicates,
157
+ max: props.max
158
+ });
159
+ if (added.length > 0) {
160
+ setTags(nextTags);
161
+ added.forEach((tag) => emit("add", tag));
162
+ }
163
+ return added;
164
+ };
165
+ const removeAt = (index) => {
166
+ const tag = tags.value[index];
167
+ if (tag === void 0) return;
168
+ setTags(removeTagAt(tags.value, index));
169
+ emit("remove", tag, index);
170
+ };
171
+ const handleInput = (event) => {
172
+ if (!isInteractive.value) return;
173
+ highlightedIndex.value = null;
174
+ const target = event.target;
175
+ const { candidates, pending } = extractTagCandidates(target.value, props.delimiters);
176
+ if (candidates.length > 0) commitCandidates(candidates);
177
+ inputText.value = pending;
178
+ target.value = pending;
179
+ };
180
+ const handleKeydown = (event) => {
181
+ if (!isInteractive.value) return;
182
+ if (event.key === "Enter") {
183
+ event.preventDefault();
184
+ if (inputText.value.trim()) {
185
+ const added = commitCandidates([inputText.value]);
186
+ if (added.length > 0) inputText.value = "";
187
+ }
188
+ return;
189
+ }
190
+ if (event.key === "Backspace" && inputText.value === "") {
191
+ if (highlightedIndex.value !== null) {
192
+ removeAt(highlightedIndex.value);
193
+ highlightedIndex.value = null;
194
+ } else if (tags.value.length > 0) {
195
+ highlightedIndex.value = tags.value.length - 1;
196
+ }
197
+ return;
198
+ }
199
+ if (event.key === "ArrowLeft" && inputText.value === "" && tags.value.length > 0) {
200
+ highlightedIndex.value = highlightedIndex.value === null ? tags.value.length - 1 : Math.max(0, highlightedIndex.value - 1);
201
+ return;
202
+ }
203
+ if (event.key === "ArrowRight" && highlightedIndex.value !== null) {
204
+ highlightedIndex.value = highlightedIndex.value >= tags.value.length - 1 ? null : highlightedIndex.value + 1;
205
+ return;
206
+ }
207
+ if (event.key === "Escape" && highlightedIndex.value !== null) {
208
+ highlightedIndex.value = null;
209
+ return;
210
+ }
211
+ if (highlightedIndex.value !== null) highlightedIndex.value = null;
212
+ };
213
+ const handlePaste = (event) => {
214
+ if (!isInteractive.value) return;
215
+ const text = event.clipboardData?.getData("text") ?? "";
216
+ const candidates = splitTagInput(text, props.delimiters);
217
+ if (candidates.length <= 1) return;
218
+ event.preventDefault();
219
+ commitCandidates(candidates);
220
+ inputText.value = "";
221
+ if (inputRef.value) inputRef.value.value = "";
222
+ };
223
+ const handleFocus = (event) => emit("focus", event);
224
+ const handleBlur = (event) => {
225
+ highlightedIndex.value = null;
226
+ if (props.addOnBlur && inputText.value.trim() && isInteractive.value) {
227
+ const added = commitCandidates([inputText.value]);
228
+ if (added.length > 0) inputText.value = "";
229
+ }
230
+ emit("blur", event);
231
+ };
232
+ const handleClear = () => {
233
+ if (!isInteractive.value || tags.value.length === 0) return;
234
+ setTags([]);
235
+ inputText.value = "";
236
+ highlightedIndex.value = null;
237
+ emit("clear");
238
+ inputRef.value?.focus();
239
+ };
240
+ const handleContainerClick = (event) => {
241
+ if (event.target === containerRef.value) inputRef.value?.focus();
242
+ };
243
+ return () => {
244
+ const { class: attrClass, style: attrStyle, ...restAttrs } = attrs;
245
+ const activeError = effectiveStatus.value === "error" && !!effectiveErrorMessage.value;
246
+ const isFull = props.max !== void 0 && tags.value.length >= props.max;
247
+ const removeLabelTemplate = props.removeTagAriaLabel ?? labels.value.removeTagLabel;
248
+ const tagSize = props.size === "lg" ? "md" : "sm";
249
+ const children = [];
250
+ tags.value.forEach((tag, index) => {
251
+ children.push(
252
+ h(
253
+ Tag,
254
+ {
255
+ key: `${tag}-${index}`,
256
+ size: tagSize,
257
+ closable: isInteractive.value,
258
+ closeAriaLabel: formatRemoveTagLabel(removeLabelTemplate, tag),
259
+ class: index === highlightedIndex.value ? getTagsInputHighlightClasses() : void 0,
260
+ onClose: (event) => {
261
+ event.preventDefault();
262
+ removeAt(index);
263
+ }
264
+ },
265
+ () => tag
266
+ )
267
+ );
268
+ });
269
+ children.push(
270
+ h("input", {
271
+ key: "text-input",
272
+ ref: inputRef,
273
+ class: getTagsInputInnerInputClasses(),
274
+ type: "text",
275
+ value: inputText.value,
276
+ placeholder: tags.value.length === 0 ? props.placeholder : "",
277
+ disabled: props.disabled,
278
+ readonly: props.readonly,
279
+ id: props.id ? `${props.id}-input` : props.id,
280
+ ...effectiveStatus.value === "error" ? { "aria-invalid": true } : {},
281
+ ...activeError ? { "aria-describedby": errorMsgId } : {},
282
+ onInput: handleInput,
283
+ onKeydown: handleKeydown,
284
+ onPaste: handlePaste,
285
+ onFocus: handleFocus,
286
+ onBlur: handleBlur
287
+ })
288
+ );
289
+ if (props.clearable && tags.value.length > 0 && isInteractive.value) {
290
+ children.push(
291
+ h(
292
+ "button",
293
+ {
294
+ key: "clear",
295
+ type: "button",
296
+ class: getTagsInputClearButtonClasses(),
297
+ onClick: handleClear,
298
+ "aria-label": labels.value.clearAllLabel,
299
+ tabindex: -1
300
+ },
301
+ "\u2715"
302
+ )
303
+ );
304
+ }
305
+ if (props.name) {
306
+ children.push(
307
+ h("input", {
308
+ key: "hidden",
309
+ type: "hidden",
310
+ name: props.name,
311
+ value: tags.value.join(",")
312
+ })
313
+ );
314
+ }
315
+ const containerNode = h(
316
+ "div",
317
+ {
318
+ ...restAttrs,
319
+ ref: containerRef,
320
+ id: props.id,
321
+ class: classNames(
322
+ getTagsInputContainerClasses(props.size, effectiveStatus.value, {
323
+ disabled: props.disabled
324
+ }),
325
+ props.className,
326
+ coerceClassValue(attrClass)
327
+ ),
328
+ style: mergeStyleValues(props.style, attrStyle),
329
+ "data-state": isFull ? "full" : void 0,
330
+ onClick: handleContainerClick
331
+ },
332
+ children
333
+ );
334
+ if (!activeError) return containerNode;
335
+ return h("div", {}, [
336
+ containerNode,
337
+ h("div", { id: errorMsgId, class: getTagsInputErrorClasses() }, effectiveErrorMessage.value)
338
+ ]);
339
+ };
340
+ }
341
+ });
342
+ var TagsInput_default = TagsInput;
343
+
344
+ export {
345
+ TagsInput,
346
+ TagsInput_default
347
+ };
@@ -0,0 +1,368 @@
1
+ // src/components/ImageCompare.ts
2
+ import { defineComponent, h, onBeforeUnmount, ref, watch } from "vue";
3
+ import {
4
+ composeComponentClasses,
5
+ getImageCompareAfterClasses,
6
+ getImageCompareBeforeClasses,
7
+ getImageCompareClipStyle,
8
+ getImageCompareHandleClasses,
9
+ getImageCompareHandleStyle,
10
+ getImageCompareImgClasses,
11
+ getImageCompareKeyboardPosition,
12
+ getImageCompareKnobClasses,
13
+ getImageCompareLineClasses,
14
+ getImageComparePositionFromPointer,
15
+ getImageCompareRootClasses,
16
+ getImageCompareRootStyle,
17
+ getImageComparePointerClientPoint,
18
+ isImageCompareInteractiveTarget,
19
+ isImageCompareVertical,
20
+ mergeStyleValues,
21
+ resolveImageCompareAriaLabel,
22
+ resolveImageCompareFit,
23
+ resolveImageCompareOrientation,
24
+ resolveImageComparePosition,
25
+ resolveImageCompareStep
26
+ } from "@expcat/tigercat-core";
27
+ function callAttrHandler(handler, event) {
28
+ if (typeof handler === "function") {
29
+ const listener = handler;
30
+ listener(event);
31
+ }
32
+ }
33
+ var ImageCompare = defineComponent({
34
+ name: "TigerImageCompare",
35
+ inheritAttrs: false,
36
+ props: {
37
+ /**
38
+ * Before (starting) image URL. The `before` slot takes precedence when provided.
39
+ */
40
+ beforeSrc: {
41
+ type: String,
42
+ default: void 0
43
+ },
44
+ /**
45
+ * After (ending) image URL. The `after` slot takes precedence when provided.
46
+ */
47
+ afterSrc: {
48
+ type: String,
49
+ default: void 0
50
+ },
51
+ /**
52
+ * Alternative text for the before image
53
+ * @default ''
54
+ */
55
+ beforeAlt: {
56
+ type: String,
57
+ default: ""
58
+ },
59
+ /**
60
+ * Alternative text for the after image
61
+ * @default ''
62
+ */
63
+ afterAlt: {
64
+ type: String,
65
+ default: ""
66
+ },
67
+ /**
68
+ * Object-fit applied to the before/after `<img>` elements
69
+ * @default 'cover'
70
+ */
71
+ fit: {
72
+ type: String,
73
+ default: "cover"
74
+ },
75
+ /**
76
+ * Handle position as a percentage of the before image that is visible
77
+ * (controlled mode)
78
+ */
79
+ position: {
80
+ type: Number,
81
+ default: void 0
82
+ },
83
+ /**
84
+ * Initial handle position (uncontrolled mode)
85
+ * @default 50
86
+ */
87
+ defaultPosition: {
88
+ type: Number,
89
+ default: void 0
90
+ },
91
+ /**
92
+ * Comparison axis
93
+ * @default 'horizontal'
94
+ */
95
+ orientation: {
96
+ type: String,
97
+ default: "horizontal"
98
+ },
99
+ /**
100
+ * Keyboard and pointer snap increment, in percentage points
101
+ * @default 1
102
+ */
103
+ step: {
104
+ type: Number,
105
+ default: void 0
106
+ },
107
+ /**
108
+ * Whether pointer and keyboard adjustment are disabled
109
+ * @default false
110
+ */
111
+ disabled: {
112
+ type: Boolean,
113
+ default: false
114
+ },
115
+ /**
116
+ * Root width (CSS value). A number is treated as pixels.
117
+ */
118
+ width: {
119
+ type: [Number, String],
120
+ default: void 0
121
+ },
122
+ /**
123
+ * Root height (CSS value). A number is treated as pixels.
124
+ */
125
+ height: {
126
+ type: [Number, String],
127
+ default: void 0
128
+ },
129
+ /**
130
+ * Accessible name for the comparison handle
131
+ * @default 'Image comparison'
132
+ */
133
+ ariaLabel: {
134
+ type: String,
135
+ default: void 0
136
+ },
137
+ /**
138
+ * Additional CSS classes
139
+ */
140
+ className: {
141
+ type: String,
142
+ default: void 0
143
+ },
144
+ /**
145
+ * Inline styles
146
+ */
147
+ style: {
148
+ type: Object,
149
+ default: void 0
150
+ }
151
+ },
152
+ emits: {
153
+ /**
154
+ * Emitted when the handle position changes (for v-model:position)
155
+ */
156
+ "update:position": (value) => typeof value === "number",
157
+ /**
158
+ * Emitted when the handle position changes
159
+ */
160
+ change: (value) => typeof value === "number"
161
+ },
162
+ setup(props, { slots, emit, attrs }) {
163
+ const rootRef = ref(null);
164
+ const handleRef = ref(null);
165
+ const dragging = ref(false);
166
+ const internalPosition = ref(
167
+ resolveImageComparePosition(props.position ?? props.defaultPosition, props.step)
168
+ );
169
+ watch(
170
+ () => props.position,
171
+ (value) => {
172
+ if (value !== void 0) {
173
+ internalPosition.value = resolveImageComparePosition(value, props.step);
174
+ }
175
+ }
176
+ );
177
+ const currentPosition = () => resolveImageComparePosition(
178
+ props.position !== void 0 ? props.position : internalPosition.value,
179
+ props.step
180
+ );
181
+ const commit = (next) => {
182
+ const resolved = resolveImageComparePosition(next, props.step);
183
+ if (props.position === void 0) {
184
+ internalPosition.value = resolved;
185
+ }
186
+ emit("update:position", resolved);
187
+ emit("change", resolved);
188
+ };
189
+ const positionFromEvent = (event) => {
190
+ const root = rootRef.value;
191
+ const point = getImageComparePointerClientPoint(event);
192
+ if (!root || !point) return null;
193
+ return getImageComparePositionFromPointer({
194
+ clientX: point.clientX,
195
+ clientY: point.clientY,
196
+ rect: root.getBoundingClientRect(),
197
+ orientation: props.orientation,
198
+ step: props.step
199
+ });
200
+ };
201
+ const handleMove = (event) => {
202
+ if (props.disabled || !dragging.value) return;
203
+ const next = positionFromEvent(event);
204
+ if (next === null) return;
205
+ commit(next);
206
+ };
207
+ const handleEnd = () => {
208
+ dragging.value = false;
209
+ document.removeEventListener("mousemove", handleMove);
210
+ document.removeEventListener("mouseup", handleEnd);
211
+ document.removeEventListener("touchmove", handleMove);
212
+ document.removeEventListener("touchend", handleEnd);
213
+ };
214
+ const startDrag = (event) => {
215
+ if (props.disabled) return;
216
+ if ("button" in event && event.button !== 0) return;
217
+ if (isImageCompareInteractiveTarget(event.target, handleRef.value)) return;
218
+ event.preventDefault();
219
+ const alreadyDragging = dragging.value;
220
+ dragging.value = true;
221
+ const next = positionFromEvent(event);
222
+ if (next !== null) commit(next);
223
+ handleRef.value?.focus();
224
+ if (alreadyDragging) return;
225
+ document.addEventListener("mousemove", handleMove);
226
+ document.addEventListener("mouseup", handleEnd);
227
+ document.addEventListener("touchmove", handleMove);
228
+ document.addEventListener("touchend", handleEnd);
229
+ };
230
+ onBeforeUnmount(handleEnd);
231
+ const renderPaneContent = (slot, src, alt) => {
232
+ if (slot) return slot();
233
+ if (!src) return null;
234
+ return h("img", {
235
+ src,
236
+ alt,
237
+ class: getImageCompareImgClasses(resolveImageCompareFit(props.fit)),
238
+ draggable: false
239
+ });
240
+ };
241
+ return () => {
242
+ const attrsRecord = attrs;
243
+ const orientation = resolveImageCompareOrientation(props.orientation);
244
+ const step = resolveImageCompareStep(props.step);
245
+ const position = currentPosition();
246
+ const vertical = isImageCompareVertical(orientation);
247
+ const attrAriaLabel = typeof attrsRecord["aria-label"] === "string" ? attrsRecord["aria-label"] : void 0;
248
+ const ariaLabel = resolveImageCompareAriaLabel(attrAriaLabel ?? props.ariaLabel);
249
+ const {
250
+ class: _class,
251
+ style: _style,
252
+ "aria-label": _ariaLabel,
253
+ "aria-labelledby": ariaLabelledby,
254
+ "aria-describedby": ariaDescribedby,
255
+ onMousedown,
256
+ onMouseDown,
257
+ onTouchstart,
258
+ onTouchStart,
259
+ ...restAttrs
260
+ } = attrsRecord;
261
+ return h(
262
+ "div",
263
+ {
264
+ ...restAttrs,
265
+ ref: rootRef,
266
+ class: composeComponentClasses(
267
+ getImageCompareRootClasses({
268
+ orientation,
269
+ disabled: props.disabled,
270
+ className: props.className
271
+ }),
272
+ attrsRecord.class
273
+ ),
274
+ style: mergeStyleValues(
275
+ getImageCompareRootStyle({
276
+ position,
277
+ step,
278
+ width: props.width,
279
+ height: props.height
280
+ }),
281
+ attrsRecord.style,
282
+ props.style
283
+ ),
284
+ "data-image-compare": "",
285
+ "data-image-compare-orientation": orientation,
286
+ "data-image-compare-position": String(position),
287
+ "data-image-compare-disabled": props.disabled ? "true" : "false",
288
+ "data-image-compare-dragging": dragging.value ? "true" : "false",
289
+ onMousedown: (event) => {
290
+ startDrag(event);
291
+ callAttrHandler(onMousedown ?? onMouseDown, event);
292
+ },
293
+ onTouchstart: (event) => {
294
+ startDrag(event);
295
+ callAttrHandler(onTouchstart ?? onTouchStart, event);
296
+ }
297
+ },
298
+ [
299
+ h(
300
+ "div",
301
+ {
302
+ class: getImageCompareAfterClasses(),
303
+ "data-image-compare-after": ""
304
+ },
305
+ [renderPaneContent(slots.after, props.afterSrc, props.afterAlt)]
306
+ ),
307
+ h(
308
+ "div",
309
+ {
310
+ class: getImageCompareBeforeClasses(),
311
+ style: getImageCompareClipStyle(position, orientation, step),
312
+ "data-image-compare-before": ""
313
+ },
314
+ [renderPaneContent(slots.before, props.beforeSrc, props.beforeAlt)]
315
+ ),
316
+ h(
317
+ "div",
318
+ {
319
+ ref: handleRef,
320
+ class: getImageCompareHandleClasses({
321
+ orientation,
322
+ disabled: props.disabled
323
+ }),
324
+ style: getImageCompareHandleStyle(position, orientation, step),
325
+ "data-image-compare-handle": "",
326
+ role: "slider",
327
+ tabindex: props.disabled ? -1 : 0,
328
+ "aria-label": ariaLabel,
329
+ "aria-labelledby": ariaLabelledby,
330
+ "aria-describedby": ariaDescribedby,
331
+ "aria-valuemin": 0,
332
+ "aria-valuemax": 100,
333
+ "aria-valuenow": position,
334
+ "aria-valuetext": `${position}%`,
335
+ "aria-orientation": vertical ? "vertical" : "horizontal",
336
+ "aria-disabled": props.disabled,
337
+ onKeydown: (event) => {
338
+ if (props.disabled) return;
339
+ const next = getImageCompareKeyboardPosition(event.key, position, step);
340
+ if (next === null) return;
341
+ event.preventDefault();
342
+ commit(next);
343
+ }
344
+ },
345
+ [
346
+ h("div", {
347
+ class: getImageCompareLineClasses(orientation),
348
+ "data-image-compare-line": "",
349
+ "aria-hidden": "true"
350
+ }),
351
+ h("div", {
352
+ class: getImageCompareKnobClasses(),
353
+ "data-image-compare-knob": "",
354
+ "aria-hidden": "true"
355
+ })
356
+ ]
357
+ )
358
+ ]
359
+ );
360
+ };
361
+ }
362
+ });
363
+ var ImageCompare_default = ImageCompare;
364
+
365
+ export {
366
+ ImageCompare,
367
+ ImageCompare_default
368
+ };