@leavepulse/ui 0.78.1 → 0.78.2

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 (146) hide show
  1. package/dist/chunks/{DropzoneLinkField-CLLhd3WB.js → DropzoneLinkField-CyE7TJ5f.js} +1 -1
  2. package/dist/chunks/LpAddressInput-ft8fQOGx.js +401 -0
  3. package/dist/chunks/{LpAppShell-BKbYCHkx.js → LpAppShell-DkvU6IBB.js} +1 -1
  4. package/dist/chunks/LpAutocomplete-CrZbd0uv.js +310 -0
  5. package/dist/chunks/LpCheckbox-CCWCwD0y.js +129 -0
  6. package/dist/chunks/{LpCodeDisplay-D3BUAhV1.js → LpCodeDisplay-D59uPwRb.js} +1 -1
  7. package/dist/chunks/LpColorPicker-BONURBFN.js +574 -0
  8. package/dist/chunks/LpDatePicker-C2AOtxgF.js +171 -0
  9. package/dist/chunks/{LpDateRangePicker-CNQXdTVv.js → LpDateRangePicker-CiXdIAyh.js} +43 -35
  10. package/dist/chunks/{LpFileTree-C-rJKzEQ.js → LpFileTree-CpNurwZp.js} +2 -2
  11. package/dist/chunks/{LpFileTreeNode-BgYn4U1J.js → LpFileTreeNode-DdF8Cq-s.js} +1 -1
  12. package/dist/chunks/{LpInput-D0O6dpuA.js → LpInput-CbI7z5fQ.js} +3 -4
  13. package/dist/chunks/{LpLogInspector-BdQlX6S-.js → LpLogInspector-CcSJSGan.js} +1 -1
  14. package/dist/chunks/{LpLogViewer-umCZBapN.js → LpLogViewer-CWu49Dko.js} +2 -2
  15. package/dist/chunks/{LpModalNav-gOi1Giz1.js → LpModalNav-DI3BW5ff.js} +1 -1
  16. package/dist/chunks/LpNumberField-C8LfUz_R.js +154 -0
  17. package/dist/chunks/LpOtpInput-B1X9l9no.js +140 -0
  18. package/dist/chunks/{LpPaletteTheme-BIXpZLGe.js → LpPaletteTheme-B-xuZiKK.js} +2 -2
  19. package/dist/chunks/{LpPasswordInput-D4HPhqfV.js → LpPasswordInput-Duc_3wPv.js} +1 -1
  20. package/dist/chunks/{LpPhoneInput-BSL7NX4z.js → LpPhoneInput-CctcRK9K.js} +124 -118
  21. package/dist/chunks/LpRadioGroup-DTM0nd9G.js +103 -0
  22. package/dist/chunks/LpRangeSlider-DJsEZXad.js +97 -0
  23. package/dist/chunks/LpRepeater-Czib3rmv.js +193 -0
  24. package/dist/chunks/{LpSecretField-B2roc4Iz.js → LpSecretField-BtlCk3IH.js} +1 -1
  25. package/dist/chunks/LpSegmented-sskrJKJJ.js +128 -0
  26. package/dist/chunks/LpSelect-JFZehOkd.js +515 -0
  27. package/dist/chunks/{LpSidebar-DDNJzb69.js → LpSidebar-CvRvVBTt.js} +1 -1
  28. package/dist/chunks/LpSlider-C4ilCtZX.js +76 -0
  29. package/dist/chunks/LpSwitch-DyAYTchz.js +122 -0
  30. package/dist/chunks/{LpTableFilter-B_XMOeAh.js → LpTableFilter-CJg5eIcV.js} +2 -2
  31. package/dist/chunks/{LpTagsInput-Dt0mteSj.js → LpTagsInput-CkNxpNYa.js} +4 -3
  32. package/dist/chunks/{LpTextarea-Co0b9PVx.js → LpTextarea-CFK_5QB8.js} +3 -2
  33. package/dist/chunks/{LpThemeStudio-ChU0_Ga8.js → LpThemeStudio-R-wohbCY.js} +2 -2
  34. package/dist/chunks/{LpThemeSwitcher-DmCZ21P_.js → LpThemeSwitcher-Bzy15MCP.js} +1 -1
  35. package/dist/chunks/LpTimeField-B5n0m8m0.js +115 -0
  36. package/dist/chunks/{LpTopologyCanvas-jqz1KCq2.js → LpTopologyCanvas-BNgw9RyZ.js} +2 -2
  37. package/dist/chunks/{LpWorkspaceSwitcher-BeNHCLd5.js → LpWorkspaceSwitcher-ugv-fyig.js} +1 -1
  38. package/dist/components/DropzoneLinkField.vue.js +1 -1
  39. package/dist/components/LpAddressInput.vue.d.ts +4 -0
  40. package/dist/components/LpAddressInput.vue.js +1 -1
  41. package/dist/components/LpAlert.vue.d.ts +3 -3
  42. package/dist/components/LpAppShell.vue.d.ts +1 -1
  43. package/dist/components/LpAppShell.vue.js +1 -1
  44. package/dist/components/LpAutocomplete.vue.d.ts +4 -0
  45. package/dist/components/LpAutocomplete.vue.js +1 -1
  46. package/dist/components/LpBadge.vue.d.ts +2 -2
  47. package/dist/components/LpChart.vue.d.ts +1 -1
  48. package/dist/components/LpCheckbox.vue.d.ts +4 -0
  49. package/dist/components/LpCheckbox.vue.js +1 -1
  50. package/dist/components/LpCodeDisplay.vue.js +1 -1
  51. package/dist/components/LpCollapse.vue.d.ts +1 -1
  52. package/dist/components/LpColorPicker.vue.d.ts +5 -3
  53. package/dist/components/LpColorPicker.vue.js +1 -1
  54. package/dist/components/LpDatePicker.vue.js +1 -1
  55. package/dist/components/LpDateRangePicker.vue.js +1 -1
  56. package/dist/components/LpDropzone.vue.js +1 -1
  57. package/dist/components/LpFileTree.vue.js +1 -1
  58. package/dist/components/LpFileTreeNode.vue.js +1 -1
  59. package/dist/components/LpImageEditor.vue.d.ts +1 -1
  60. package/dist/components/LpInput.vue.js +1 -1
  61. package/dist/components/LpLogInspector.vue.js +1 -1
  62. package/dist/components/LpLogViewer.vue.js +1 -1
  63. package/dist/components/LpModalNav.vue.js +1 -1
  64. package/dist/components/LpNumberField.vue.d.ts +4 -0
  65. package/dist/components/LpNumberField.vue.js +1 -1
  66. package/dist/components/LpOtpInput.vue.d.ts +4 -0
  67. package/dist/components/LpOtpInput.vue.js +1 -1
  68. package/dist/components/LpPaletteTheme.vue.js +1 -1
  69. package/dist/components/LpPasswordInput.vue.js +1 -1
  70. package/dist/components/LpPhoneInput.vue.d.ts +4 -0
  71. package/dist/components/LpPhoneInput.vue.js +1 -1
  72. package/dist/components/LpRadioGroup.vue.d.ts +4 -0
  73. package/dist/components/LpRadioGroup.vue.js +1 -1
  74. package/dist/components/LpRangeSlider.vue.d.ts +4 -0
  75. package/dist/components/LpRangeSlider.vue.js +1 -1
  76. package/dist/components/LpRepeater.vue.d.ts +2 -0
  77. package/dist/components/LpRepeater.vue.js +1 -1
  78. package/dist/components/LpScrollArea.vue.d.ts +1 -1
  79. package/dist/components/LpSecretField.vue.js +1 -1
  80. package/dist/components/LpSegmented.vue.d.ts +4 -0
  81. package/dist/components/LpSegmented.vue.js +1 -1
  82. package/dist/components/LpSelect.vue.d.ts +2 -0
  83. package/dist/components/LpSelect.vue.js +1 -1
  84. package/dist/components/LpShinyText.vue.d.ts +1 -1
  85. package/dist/components/LpSidebar.vue.d.ts +1 -1
  86. package/dist/components/LpSidebar.vue.js +1 -1
  87. package/dist/components/LpSlider.vue.d.ts +4 -0
  88. package/dist/components/LpSlider.vue.js +1 -1
  89. package/dist/components/LpSwitch.vue.d.ts +4 -0
  90. package/dist/components/LpSwitch.vue.js +1 -1
  91. package/dist/components/LpTable.vue.js +2 -2
  92. package/dist/components/LpTableFilter.vue.js +1 -1
  93. package/dist/components/LpTabs.vue.d.ts +1 -1
  94. package/dist/components/LpTagsInput.vue.js +1 -1
  95. package/dist/components/LpTextarea.vue.js +1 -1
  96. package/dist/components/LpThemeStudio.vue.js +1 -1
  97. package/dist/components/LpThemeSwitcher.vue.js +1 -1
  98. package/dist/components/LpTimeField.vue.d.ts +4 -0
  99. package/dist/components/LpTimeField.vue.js +1 -1
  100. package/dist/components/LpTopologyCanvas.vue.js +1 -1
  101. package/dist/components/LpWorkspaceSwitcher.vue.js +1 -1
  102. package/dist/components/calendar.d.ts +2 -0
  103. package/dist/composables/useField.d.ts +50 -0
  104. package/dist/composables/useField.js +31 -2
  105. package/dist/composables/useMergedClass.d.ts +6 -2
  106. package/dist/composables/useMergedClass.js +6 -6
  107. package/dist/index.js +34 -34
  108. package/package.json +1 -1
  109. package/src/components/LpAddressInput.vue +8 -4
  110. package/src/components/LpAutocomplete.vue +11 -5
  111. package/src/components/LpCheckbox.vue +8 -6
  112. package/src/components/LpColorPicker.vue +7 -4
  113. package/src/components/LpDatePicker.vue +8 -4
  114. package/src/components/LpDateRangePicker.vue +8 -4
  115. package/src/components/LpInput.vue +3 -4
  116. package/src/components/LpNumberField.vue +11 -6
  117. package/src/components/LpOtpInput.vue +8 -6
  118. package/src/components/LpPhoneInput.vue +11 -6
  119. package/src/components/LpRadioGroup.vue +9 -5
  120. package/src/components/LpRangeSlider.vue +8 -4
  121. package/src/components/LpRepeater.vue +16 -4
  122. package/src/components/LpSegmented.vue +8 -4
  123. package/src/components/LpSelect.vue +8 -7
  124. package/src/components/LpSlider.vue +8 -4
  125. package/src/components/LpSwitch.vue +8 -5
  126. package/src/components/LpTagsInput.vue +4 -3
  127. package/src/components/LpTextarea.vue +3 -2
  128. package/src/components/LpTimeField.vue +8 -6
  129. package/src/components/calendar.ts +2 -0
  130. package/src/composables/useField.ts +52 -0
  131. package/src/composables/useMergedClass.ts +11 -5
  132. package/dist/chunks/LpAddressInput-BSS49jpm.js +0 -393
  133. package/dist/chunks/LpAutocomplete-DkY6CSyD.js +0 -302
  134. package/dist/chunks/LpCheckbox-D6RuDW4X.js +0 -124
  135. package/dist/chunks/LpColorPicker-COHzV68R.js +0 -567
  136. package/dist/chunks/LpDatePicker-DwSDY0mt.js +0 -163
  137. package/dist/chunks/LpNumberField-Csg-1Zh_.js +0 -148
  138. package/dist/chunks/LpOtpInput-BRpLBbOD.js +0 -132
  139. package/dist/chunks/LpRadioGroup-BrazjLY_.js +0 -95
  140. package/dist/chunks/LpRangeSlider-fz61tzc-.js +0 -89
  141. package/dist/chunks/LpRepeater-BkJHSPI1.js +0 -180
  142. package/dist/chunks/LpSegmented-DbEJhbv-.js +0 -123
  143. package/dist/chunks/LpSelect-Y-iuSymW.js +0 -512
  144. package/dist/chunks/LpSlider-A4ixovgS.js +0 -68
  145. package/dist/chunks/LpSwitch-DnC2-0aV.js +0 -115
  146. package/dist/chunks/LpTimeField-DeOcKWJq.js +0 -110
@@ -0,0 +1,574 @@
1
+ import { defineComponent, ref, watch, computed, useAttrs, openBlock, createBlock, normalizeProps, guardReactiveProps, unref, withCtx, createVNode, normalizeClass, createElementBlock, normalizeStyle, createElementVNode, createCommentVNode, toDisplayString, withDirectives, vShow, withKeys, Fragment, renderList, renderSlot, nextTick } from "vue";
2
+ import { _ as _sfc_main$1 } from "./FieldWrapper-DnuMTrqW.js";
3
+ import { CONTROL_SIZE } from "../components/controlSize.js";
4
+ import { clamp } from "../composables/clamp.js";
5
+ import { PopoverRoot, PopoverAnchor, PopoverTrigger, PopoverPortal, PopoverContent } from "reka-ui";
6
+ import { useKitStorage } from "../composables/usePersisted.js";
7
+ import { prefersReducedMotion } from "../composables/easing.js";
8
+ import { useOwnField, mergeDescribedBy } from "../composables/useField.js";
9
+ import { DEFAULT_SWATCHES, formatColor, opaqueCss, parseColor, rgbToHsv, isLight, clampHsv } from "../components/color.js";
10
+ import { useClipboard } from "../composables/useClipboard.js";
11
+ import { useLabels } from "../composables/useLpConfig.js";
12
+ import { POPOVER_PANEL } from "../components/dropdown.js";
13
+ import { _ as _sfc_main$2 } from "./LpContextMenu-Bx_IhQDS.js";
14
+ import { _ as _sfc_main$3 } from "./LpIcon-C6JpQ5td.js";
15
+ import { _ as _sfc_main$4 } from "./LpRollingText-B54Pje7P.js";
16
+ import { usePortalTarget, useOutsideGuard } from "../composables/useView.js";
17
+ const _hoisted_1 = ["id", "disabled", "aria-label", "title"];
18
+ const _hoisted_2 = ["id", "disabled", "aria-label", "aria-invalid", "aria-describedby"];
19
+ const _hoisted_3 = { class: "min-w-0 flex-1 truncate font-mono text-xs text-muted-strong" };
20
+ const _hoisted_4 = { class: "flex flex-col gap-3" };
21
+ const _hoisted_5 = ["tabindex", "aria-label", "aria-valuenow", "aria-valuetext"];
22
+ const _hoisted_6 = ["aria-label", "aria-valuenow", "tabindex"];
23
+ const _hoisted_7 = ["aria-label", "aria-valuenow", "tabindex"];
24
+ const _hoisted_8 = {
25
+ key: 1,
26
+ "data-lp-ring-owner": "",
27
+ class: "flex h-(--size-control-sm) items-center gap-2 overflow-hidden rounded-control border border-line bg-surface-soft px-2 transition-colors duration-[var(--duration-fast)] focus-within:border-brand focus-within:ring-2 focus-within:ring-ring"
28
+ };
29
+ const _hoisted_9 = { class: "relative flex min-w-0 flex-1 items-center" };
30
+ const _hoisted_10 = ["value", "disabled", "aria-label"];
31
+ const _hoisted_11 = {
32
+ key: 0,
33
+ class: "text-[11px] font-medium uppercase tracking-wide text-muted"
34
+ };
35
+ const _hoisted_12 = ["disabled", "aria-label", "aria-pressed", "onClick"];
36
+ const ANY_COLOUR = "conic-gradient(from 90deg, #ff5c5c, #ffd23f, #39e58c, #2dd4bf, #00bcff, #7c5cff, #ff5ca8, #ff5c5c)";
37
+ const CHECKERBOARD = "repeating-conic-gradient(var(--color-line-strong) 0% 25%, transparent 0% 50%) 0 0 / 12px 12px";
38
+ const ROLL_MS = 320;
39
+ const _sfc_main = /* @__PURE__ */ defineComponent({
40
+ __name: "LpColorPicker",
41
+ props: {
42
+ modelValue: { default: "#3b82f6" },
43
+ format: { default: "hex" },
44
+ alpha: { type: Boolean, default: false },
45
+ swatches: { default: () => DEFAULT_SWATCHES },
46
+ hideInput: { type: Boolean },
47
+ disabled: { type: Boolean },
48
+ invalid: { type: Boolean },
49
+ error: {},
50
+ size: { default: "md" },
51
+ label: {},
52
+ variant: { default: "field" },
53
+ selected: { type: Boolean, default: true },
54
+ ariaLabel: {},
55
+ side: { default: "bottom" },
56
+ align: { default: "start" },
57
+ menu: {},
58
+ menuItems: {},
59
+ recent: { type: Boolean, default: false },
60
+ recentKey: { default: "lp.color.recent" },
61
+ recentLimit: { default: 8 },
62
+ labels: {},
63
+ copyToast: { type: [String, Boolean] }
64
+ },
65
+ emits: ["update:modelValue", "change"],
66
+ setup(__props, { expose: __expose, emit: __emit }) {
67
+ const props = __props;
68
+ const emit = __emit;
69
+ function fill(tpl, vars) {
70
+ return tpl.replace(/\{(\w+)\}/g, (_, k) => String(vars[k] ?? ""));
71
+ }
72
+ const l = useLabels(
73
+ "colorPicker",
74
+ {
75
+ saturation: "Saturation and brightness",
76
+ hue: "Hue",
77
+ opacity: "Opacity",
78
+ value: "Colour value",
79
+ recent: "Recent",
80
+ copyHex: "Copy hex",
81
+ copyRgb: "Copy rgb",
82
+ copyHsl: "Copy hsl",
83
+ paste: "Paste colour",
84
+ formatAs: "Show as",
85
+ format_hex: "HEX",
86
+ format_rgb: "RGB",
87
+ format_hsl: "HSL",
88
+ saturationValueText: "saturation {s}%, brightness {v}%",
89
+ copyToast: "Copied {value}"
90
+ },
91
+ () => props.labels
92
+ );
93
+ const { copy } = useClipboard();
94
+ const open = ref(false);
95
+ const hsv = ref({ h: 217, s: 0.76, v: 0.96, a: 1 });
96
+ function adopt(value) {
97
+ const rgb = parseColor(value ?? "");
98
+ if (!rgb) return;
99
+ const next = rgbToHsv(rgb);
100
+ if (next.s === 0) next.h = hsv.value.h;
101
+ if (next.v === 0) next.s = hsv.value.s;
102
+ hsv.value = next;
103
+ }
104
+ adopt(props.modelValue);
105
+ watch(
106
+ () => props.modelValue,
107
+ (v) => {
108
+ if (v !== current.value) adopt(v);
109
+ }
110
+ );
111
+ const activeFormat = ref(props.format);
112
+ watch(() => props.format, (f) => activeFormat.value = f);
113
+ const current = computed(
114
+ () => formatColor(props.alpha ? hsv.value : { ...hsv.value, a: 1 }, activeFormat.value)
115
+ );
116
+ function set(patch, commit = false) {
117
+ if (props.disabled) return;
118
+ hsv.value = clampHsv({ ...hsv.value, ...patch });
119
+ emit("update:modelValue", current.value);
120
+ if (commit) emit("change", current.value);
121
+ }
122
+ function trackPointer(e, onMove) {
123
+ if (props.disabled) return;
124
+ if (e.button !== 0) return;
125
+ e.preventDefault();
126
+ const el = e.currentTarget;
127
+ const rect = el.getBoundingClientRect();
128
+ const at = (ev) => {
129
+ const x = rect.width === 0 ? 0 : (ev.clientX - rect.left) / rect.width;
130
+ const y = rect.height === 0 ? 0 : (ev.clientY - rect.top) / rect.height;
131
+ onMove(clamp(x, 0, 1), clamp(y, 0, 1));
132
+ };
133
+ el.setPointerCapture(e.pointerId);
134
+ at(e);
135
+ const move = (ev) => at(ev);
136
+ const up = () => {
137
+ el.removeEventListener("pointermove", move);
138
+ el.removeEventListener("pointerup", up);
139
+ el.removeEventListener("pointercancel", up);
140
+ emit("change", current.value);
141
+ };
142
+ el.addEventListener("pointermove", move);
143
+ el.addEventListener("pointerup", up);
144
+ el.addEventListener("pointercancel", up);
145
+ }
146
+ const onFieldPointer = (e) => trackPointer(e, (x, y) => set({ s: x, v: 1 - y }));
147
+ const onHuePointer = (e) => trackPointer(e, (x) => set({ h: Math.min(x * 360, 359.9) }));
148
+ const onAlphaPointer = (e) => trackPointer(e, (x) => set({ a: x }));
149
+ function step(e, axis) {
150
+ const big = e.shiftKey;
151
+ const k = e.key;
152
+ const arrows = ["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End"];
153
+ if (!arrows.includes(k)) return;
154
+ e.preventDefault();
155
+ const dx = (k === "ArrowRight" ? 1 : k === "ArrowLeft" ? -1 : 0) * (big ? 10 : 1);
156
+ const dy = (k === "ArrowUp" ? 1 : k === "ArrowDown" ? -1 : 0) * (big ? 10 : 1);
157
+ if (axis === "h") {
158
+ if (k === "Home") return set({ h: 0 }, true);
159
+ if (k === "End") return set({ h: 359 }, true);
160
+ return set({ h: hsv.value.h + (dx || dy) }, true);
161
+ }
162
+ if (axis === "a") {
163
+ if (k === "Home") return set({ a: 0 }, true);
164
+ if (k === "End") return set({ a: 1 }, true);
165
+ return set({ a: hsv.value.a + (dx || dy) / 100 }, true);
166
+ }
167
+ if (k === "Home") return set({ s: 0 }, true);
168
+ if (k === "End") return set({ s: 1 }, true);
169
+ set({ s: hsv.value.s + dx / 100, v: hsv.value.v + dy / 100 }, true);
170
+ }
171
+ const typed = ref(null);
172
+ let typedChange = false;
173
+ const inputValue = computed(() => typed.value ?? current.value);
174
+ function onInput(e) {
175
+ const value = e.target.value;
176
+ typed.value = value;
177
+ const rgb = parseColor(value);
178
+ if (!rgb) return;
179
+ const next = rgbToHsv(rgb);
180
+ if (next.s === 0) next.h = hsv.value.h;
181
+ if (next.v === 0) next.s = hsv.value.s;
182
+ set(props.alpha ? next : { ...next, a: 1 });
183
+ typedChange = true;
184
+ }
185
+ function commitTyped() {
186
+ if (!typedChange) return;
187
+ typedChange = false;
188
+ emit("change", current.value);
189
+ }
190
+ function onInputBlur() {
191
+ typed.value = null;
192
+ commitTyped();
193
+ }
194
+ function pickSwatch(value) {
195
+ const rgb = parseColor(value);
196
+ if (!rgb) return;
197
+ typed.value = null;
198
+ set(rgbToHsv(rgb), true);
199
+ }
200
+ function isActiveSwatch(value) {
201
+ const rgb = parseColor(value);
202
+ return !!rgb && formatColor(rgbToHsv(rgb), "hex") === formatColor({ ...hsv.value, a: 1 }, "hex");
203
+ }
204
+ function tickClass(value) {
205
+ const rgb = parseColor(value);
206
+ return rgb && isLight(rgbToHsv(rgb)) ? "text-black" : "text-white";
207
+ }
208
+ const SWATCH_SIZE = { sm: "size-6", md: "size-7", lg: "size-8" };
209
+ const triggerSize = CONTROL_SIZE;
210
+ const previewCss = computed(() => formatColor(props.alpha ? hsv.value : { ...hsv.value, a: 1 }, "rgb"));
211
+ const hueCss = computed(() => `hsl(${Math.round(hsv.value.h)}, 100%, 50%)`);
212
+ const alphaRampStart = computed(() => formatColor({ ...hsv.value, a: 0 }, "rgb"));
213
+ const fieldCss = computed(() => opaqueCss({ ...hsv.value, s: 1, v: 1 }));
214
+ const fieldStyle = computed(() => ({
215
+ background: [
216
+ "linear-gradient(to top, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0))",
217
+ "linear-gradient(to right, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0))",
218
+ fieldCss.value
219
+ ].join(", ")
220
+ }));
221
+ const store = useKitStorage();
222
+ function readRecent() {
223
+ try {
224
+ const raw = JSON.parse(store.get(props.recentKey) ?? "[]");
225
+ return Array.isArray(raw) ? raw.filter((v) => typeof v === "string" && !!parseColor(v)).slice(0, props.recentLimit) : [];
226
+ } catch {
227
+ return [];
228
+ }
229
+ }
230
+ const recentColors = ref(props.recent ? readRecent() : []);
231
+ function remember(value) {
232
+ if (!props.recent) return;
233
+ const key = formatColor({ ...hsv.value, a: 1 }, "hex");
234
+ const without = recentColors.value.filter(
235
+ (c) => formatColor(rgbToHsv(parseColor(c) ?? { r: 0, g: 0, b: 0, a: 1 }), "hex") !== key
236
+ );
237
+ recentColors.value = [value, ...without].slice(0, props.recentLimit);
238
+ store.set(props.recentKey, JSON.stringify(recentColors.value));
239
+ }
240
+ let openedOn = "";
241
+ watch(open, (isOpen, was) => {
242
+ if (isOpen) openedOn = current.value;
243
+ else if (was && current.value !== openedOn) remember(current.value);
244
+ });
245
+ const swatchRows = computed(
246
+ () => [
247
+ {
248
+ key: "recent",
249
+ heading: l.value.recent,
250
+ colors: props.recent ? recentColors.value : [],
251
+ cols: "grid-cols-8",
252
+ size: "size-6"
253
+ },
254
+ {
255
+ key: "presets",
256
+ heading: "",
257
+ colors: [...props.swatches],
258
+ cols: "grid-cols-6",
259
+ size: "size-7"
260
+ }
261
+ ].filter((r) => r.colors.length)
262
+ );
263
+ function onPanelOpen(e) {
264
+ e.preventDefault();
265
+ e.currentTarget?.focus?.({ preventScroll: true });
266
+ }
267
+ function copyAs(fmt) {
268
+ const text = formatColor(props.alpha ? hsv.value : { ...hsv.value, a: 1 }, fmt);
269
+ const toast = props.copyToast ?? l.value.copyToast;
270
+ void copy(text, { toast: toast === false ? false : fill(toast, { value: text }) });
271
+ }
272
+ async function pasteColor() {
273
+ try {
274
+ const text = await navigator.clipboard.readText();
275
+ const rgb = parseColor(text);
276
+ if (!rgb) return;
277
+ typed.value = null;
278
+ const next = rgbToHsv(rgb);
279
+ set(props.alpha ? next : { ...next, a: 1 }, true);
280
+ } catch {
281
+ }
282
+ }
283
+ const rollText = ref(current.value);
284
+ const rolling = ref(false);
285
+ let rollTimer;
286
+ function setFormat(f) {
287
+ if (activeFormat.value === f) return;
288
+ rollText.value = inputValue.value;
289
+ activeFormat.value = f;
290
+ if (!prefersReducedMotion()) {
291
+ rolling.value = true;
292
+ void nextTick(() => rollText.value = current.value);
293
+ if (rollTimer) clearTimeout(rollTimer);
294
+ rollTimer = setTimeout(() => rolling.value = false, ROLL_MS + 40);
295
+ }
296
+ typed.value = null;
297
+ emit("update:modelValue", current.value);
298
+ emit("change", current.value);
299
+ }
300
+ const menuItems = computed(() => {
301
+ const own = props.menuItems ?? [];
302
+ if (props.menu === "none") return own;
303
+ const builtin = [
304
+ { label: l.value.copyHex, icon: "lucide:copy", onSelect: () => copyAs("hex") },
305
+ { label: l.value.copyRgb, icon: "lucide:copy", onSelect: () => copyAs("rgb") },
306
+ { label: l.value.copyHsl, icon: "lucide:copy", onSelect: () => copyAs("hsl") },
307
+ { label: l.value.paste, icon: "lucide:clipboard", onSelect: pasteColor },
308
+ {
309
+ // Ticks rather than a plain list: this is a setting with a current value,
310
+ // and the menu should say which one is on without being opened twice.
311
+ label: l.value.formatAs,
312
+ icon: "lucide:code",
313
+ separatorBefore: true,
314
+ children: ["hex", "rgb", "hsl"].map((f) => ({
315
+ label: l.value[`format_${f}`],
316
+ checked: activeFormat.value === f,
317
+ onSelect: () => setFormat(f)
318
+ }))
319
+ }
320
+ ];
321
+ return own.length ? [...builtin, { ...own[0], separatorBefore: true }, ...own.slice(1)] : builtin;
322
+ });
323
+ const attrs = useAttrs();
324
+ const { field, wrapper, ownAttrs } = useOwnField(() => ({
325
+ error: props.error,
326
+ invalid: props.invalid,
327
+ disabled: props.disabled
328
+ }));
329
+ __expose({ open });
330
+ const portalTo = usePortalTarget();
331
+ const guardOutside = useOutsideGuard();
332
+ return (_ctx, _cache) => {
333
+ return openBlock(), createBlock(_sfc_main$1, normalizeProps(guardReactiveProps(unref(wrapper))), {
334
+ default: withCtx(() => [
335
+ createVNode(unref(PopoverRoot), {
336
+ open: open.value,
337
+ "onUpdate:open": _cache[3] || (_cache[3] = ($event) => open.value = $event)
338
+ }, {
339
+ default: withCtx(() => [
340
+ createVNode(unref(PopoverAnchor), {
341
+ class: normalizeClass(__props.variant === "swatch" ? "inline-block" : "block w-full")
342
+ }, {
343
+ default: withCtx(() => [
344
+ createVNode(_sfc_main$2, { items: menuItems.value }, {
345
+ default: withCtx(() => [
346
+ createVNode(unref(PopoverTrigger), { "as-child": "" }, {
347
+ default: withCtx(() => [
348
+ __props.variant === "swatch" ? (openBlock(), createElementBlock("button", {
349
+ key: 0,
350
+ type: "button",
351
+ id: unref(field).id,
352
+ disabled: unref(field).disabled,
353
+ "aria-label": __props.ariaLabel ?? current.value,
354
+ title: current.value,
355
+ class: normalizeClass(["relative shrink-0 overflow-hidden rounded-full outline-none ring-2 ring-offset-2 ring-offset-surface-raised transition-[box-shadow,scale] duration-(--duration-fast) hover:scale-110 focus-visible:ring-ring data-[state=open]:ring-brand disabled:cursor-not-allowed disabled:opacity-55", [SWATCH_SIZE[__props.size], __props.selected ? "ring-ink" : "ring-line"]]),
356
+ style: normalizeStyle({ background: CHECKERBOARD })
357
+ }, [
358
+ createElementVNode("span", {
359
+ class: "block size-full",
360
+ style: normalizeStyle({ background: __props.selected ? previewCss.value : ANY_COLOUR })
361
+ }, null, 4),
362
+ !__props.selected ? (openBlock(), createBlock(_sfc_main$3, {
363
+ key: 0,
364
+ name: "lucide:pipette",
365
+ size: 12,
366
+ class: "absolute inset-0 m-auto text-white drop-shadow"
367
+ })) : createCommentVNode("", true)
368
+ ], 14, _hoisted_1)) : (openBlock(), createElementBlock("button", {
369
+ key: 1,
370
+ type: "button",
371
+ id: unref(field).id,
372
+ disabled: unref(field).disabled,
373
+ "aria-label": __props.ariaLabel,
374
+ "aria-invalid": unref(field).invalid || void 0,
375
+ "aria-describedby": unref(mergeDescribedBy)(unref(attrs)["aria-describedby"], unref(field).describedBy),
376
+ class: normalizeClass(["group inline-flex w-full items-center gap-2 rounded-control border bg-surface-soft px-2 text-left text-ink outline-none transition-colors duration-[var(--duration-fast)] focus-visible:ring-2 focus-visible:ring-ring data-[state=open]:border-brand disabled:cursor-not-allowed disabled:opacity-55", [
377
+ unref(triggerSize)[__props.size],
378
+ unref(field).invalid ? "border-danger focus-visible:ring-danger-soft" : "border-line focus-visible:border-brand"
379
+ ]])
380
+ }, [
381
+ createElementVNode("span", {
382
+ class: "relative size-5 shrink-0 overflow-hidden rounded-inner",
383
+ style: normalizeStyle({ background: CHECKERBOARD })
384
+ }, [
385
+ createElementVNode("span", {
386
+ class: "block size-full",
387
+ style: normalizeStyle({ backgroundColor: previewCss.value })
388
+ }, null, 4),
389
+ _cache[4] || (_cache[4] = createElementVNode("span", { class: "pointer-events-none absolute inset-0 rounded-inner ring-1 ring-inset ring-line" }, null, -1))
390
+ ], 4),
391
+ createElementVNode("span", _hoisted_3, toDisplayString(__props.label ?? current.value), 1),
392
+ createVNode(_sfc_main$3, {
393
+ name: "lucide:chevron-down",
394
+ size: 15,
395
+ class: "shrink-0 text-muted transition-transform duration-[var(--duration-fast)] ease-[var(--ease-emphasized)] group-data-[state=open]:rotate-180"
396
+ })
397
+ ], 10, _hoisted_2))
398
+ ]),
399
+ _: 1
400
+ })
401
+ ]),
402
+ _: 1
403
+ }, 8, ["items"])
404
+ ]),
405
+ _: 1
406
+ }, 8, ["class"]),
407
+ createVNode(unref(PopoverPortal), { to: unref(portalTo) }, {
408
+ default: withCtx(() => [
409
+ createVNode(unref(PopoverContent), {
410
+ onInteractOutside: unref(guardOutside),
411
+ onEscapeKeyDown: unref(guardOutside),
412
+ "collision-padding": 8,
413
+ side: __props.side,
414
+ align: __props.align,
415
+ "side-offset": 6,
416
+ class: normalizeClass([unref(POPOVER_PANEL), "z-(--z-popover) w-64 select-none rounded-card p-3 outline-none"]),
417
+ onOpenAutoFocus: onPanelOpen
418
+ }, {
419
+ default: withCtx(() => [
420
+ createVNode(_sfc_main$2, { items: menuItems.value }, {
421
+ default: withCtx(() => [
422
+ createElementVNode("div", _hoisted_4, [
423
+ createElementVNode("div", {
424
+ role: "slider",
425
+ tabindex: __props.disabled ? -1 : 0,
426
+ "aria-label": unref(l).saturation,
427
+ "aria-valuemin": 0,
428
+ "aria-valuemax": 100,
429
+ "aria-valuenow": Math.round(hsv.value.s * 100),
430
+ "aria-valuetext": fill(unref(l).saturationValueText, { s: Math.round(hsv.value.s * 100), v: Math.round(hsv.value.v * 100) }),
431
+ class: "relative h-36 w-full cursor-crosshair touch-none overflow-hidden rounded-control outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
432
+ style: normalizeStyle(fieldStyle.value),
433
+ onPointerdown: onFieldPointer,
434
+ onKeydown: _cache[0] || (_cache[0] = ($event) => step($event, "sv"))
435
+ }, [
436
+ createElementVNode("span", {
437
+ class: "pointer-events-none absolute size-3.5 -translate-x-1/2 -translate-y-1/2 rounded-pill border-2 border-white shadow ring-1 ring-black/30",
438
+ style: normalizeStyle({
439
+ left: `${hsv.value.s * 100}%`,
440
+ top: `${(1 - hsv.value.v) * 100}%`,
441
+ backgroundColor: previewCss.value
442
+ })
443
+ }, null, 4)
444
+ ], 44, _hoisted_5),
445
+ createElementVNode("div", {
446
+ role: "slider",
447
+ "aria-label": unref(l).hue,
448
+ "aria-valuemin": 0,
449
+ "aria-valuemax": 360,
450
+ "aria-valuenow": Math.round(hsv.value.h),
451
+ tabindex: __props.disabled ? -1 : 0,
452
+ class: "relative h-3 w-full cursor-pointer touch-none rounded-pill outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
453
+ style: { "background": "linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00)" },
454
+ onPointerdown: onHuePointer,
455
+ onKeydown: _cache[1] || (_cache[1] = ($event) => step($event, "h"))
456
+ }, [
457
+ createElementVNode("span", {
458
+ class: "pointer-events-none absolute top-1/2 size-4 -translate-x-1/2 -translate-y-1/2 rounded-pill border-2 border-white shadow ring-1 ring-black/30",
459
+ style: normalizeStyle({ left: `${hsv.value.h / 360 * 100}%`, backgroundColor: hueCss.value })
460
+ }, null, 4)
461
+ ], 40, _hoisted_6),
462
+ __props.alpha ? (openBlock(), createElementBlock("div", {
463
+ key: 0,
464
+ role: "slider",
465
+ "aria-label": unref(l).opacity,
466
+ "aria-valuemin": 0,
467
+ "aria-valuemax": 100,
468
+ "aria-valuenow": Math.round(hsv.value.a * 100),
469
+ tabindex: __props.disabled ? -1 : 0,
470
+ class: "relative h-3 w-full cursor-pointer touch-none rounded-pill outline-none ring-1 ring-inset ring-line focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
471
+ style: normalizeStyle({ background: CHECKERBOARD }),
472
+ onPointerdown: onAlphaPointer,
473
+ onKeydown: _cache[2] || (_cache[2] = ($event) => step($event, "a"))
474
+ }, [
475
+ createElementVNode("div", {
476
+ class: "absolute inset-0 overflow-hidden rounded-pill",
477
+ style: normalizeStyle({ backgroundImage: `linear-gradient(to right, ${alphaRampStart.value}, ${unref(opaqueCss)(hsv.value)})` })
478
+ }, null, 4),
479
+ createElementVNode("span", {
480
+ class: "pointer-events-none absolute top-1/2 size-4 -translate-x-1/2 -translate-y-1/2 rounded-pill border-2 border-white shadow ring-1 ring-black/30",
481
+ style: normalizeStyle({ left: `${hsv.value.a * 100}%`, backgroundColor: previewCss.value })
482
+ }, null, 4)
483
+ ], 44, _hoisted_7)) : createCommentVNode("", true),
484
+ !__props.hideInput ? (openBlock(), createElementBlock("div", _hoisted_8, [
485
+ createElementVNode("span", {
486
+ class: "size-4 shrink-0 rounded-[5px] border border-line",
487
+ style: normalizeStyle({ backgroundColor: previewCss.value })
488
+ }, null, 4),
489
+ createElementVNode("span", _hoisted_9, [
490
+ withDirectives(createVNode(_sfc_main$4, {
491
+ value: rollText.value,
492
+ duration: ROLL_MS,
493
+ align: "start",
494
+ "aria-hidden": "true",
495
+ class: "pointer-events-none absolute inset-0 font-mono text-xs text-ink"
496
+ }, null, 8, ["value"]), [
497
+ [vShow, rolling.value]
498
+ ]),
499
+ createElementVNode("input", {
500
+ value: inputValue.value,
501
+ disabled: __props.disabled,
502
+ spellcheck: "false",
503
+ autocapitalize: "off",
504
+ autocomplete: "off",
505
+ "aria-label": unref(l).value,
506
+ class: normalizeClass(["min-w-0 flex-1 select-text bg-transparent font-mono text-xs text-ink outline-none placeholder:text-muted", rolling.value ? "text-transparent" : ""]),
507
+ onInput,
508
+ onKeydown: withKeys(commitTyped, ["enter"]),
509
+ onBlur: onInputBlur
510
+ }, null, 42, _hoisted_10)
511
+ ])
512
+ ])) : createCommentVNode("", true),
513
+ (openBlock(true), createElementBlock(Fragment, null, renderList(swatchRows.value, (row) => {
514
+ return openBlock(), createElementBlock("div", {
515
+ key: row.key,
516
+ class: "flex flex-col gap-1.5"
517
+ }, [
518
+ row.heading ? (openBlock(), createElementBlock("span", _hoisted_11, toDisplayString(row.heading), 1)) : createCommentVNode("", true),
519
+ createElementVNode("div", {
520
+ class: normalizeClass(["grid gap-1.5", row.cols])
521
+ }, [
522
+ (openBlock(true), createElementBlock(Fragment, null, renderList(row.colors, (c) => {
523
+ return openBlock(), createElementBlock("button", {
524
+ key: c,
525
+ type: "button",
526
+ disabled: __props.disabled,
527
+ "aria-label": c,
528
+ "aria-pressed": isActiveSwatch(c),
529
+ class: normalizeClass(["relative flex items-center justify-center overflow-hidden rounded-inner outline-none transition-transform duration-[var(--duration-fast)] ease-[var(--ease-emphasized)] hover:scale-110 active:scale-95 focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed motion-reduce:hover:scale-100 motion-reduce:active:scale-95", row.size]),
530
+ style: normalizeStyle({ background: CHECKERBOARD }),
531
+ onClick: ($event) => pickSwatch(c)
532
+ }, [
533
+ createElementVNode("span", {
534
+ class: "absolute inset-0",
535
+ style: normalizeStyle({ backgroundColor: c })
536
+ }, null, 4),
537
+ _cache[5] || (_cache[5] = createElementVNode("span", { class: "pointer-events-none absolute inset-0 rounded-inner ring-1 ring-inset ring-line" }, null, -1)),
538
+ isActiveSwatch(c) ? (openBlock(), createBlock(_sfc_main$3, {
539
+ key: 0,
540
+ name: "lucide:check",
541
+ size: 13,
542
+ class: normalizeClass(["relative", tickClass(c)])
543
+ }, null, 8, ["class"])) : createCommentVNode("", true)
544
+ ], 14, _hoisted_12);
545
+ }), 128))
546
+ ], 2)
547
+ ]);
548
+ }), 128)),
549
+ renderSlot(_ctx.$slots, "default", {
550
+ color: current.value,
551
+ hsv: hsv.value
552
+ })
553
+ ])
554
+ ]),
555
+ _: 3
556
+ }, 8, ["items"])
557
+ ]),
558
+ _: 3
559
+ }, 8, ["onInteractOutside", "onEscapeKeyDown", "side", "align", "class"])
560
+ ]),
561
+ _: 3
562
+ }, 8, ["to"])
563
+ ]),
564
+ _: 3
565
+ }, 8, ["open"])
566
+ ]),
567
+ _: 3
568
+ }, 16);
569
+ };
570
+ }
571
+ });
572
+ export {
573
+ _sfc_main as _
574
+ };