@witchcraft/ui 0.1.1 → 0.1.3

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 (122) hide show
  1. package/dist/module.cjs +5 -0
  2. package/dist/module.d.ts +36 -0
  3. package/dist/module.json +2 -2
  4. package/dist/module.mjs +2 -1
  5. package/dist/runtime/assets/utils.css +1 -1
  6. package/dist/runtime/components/Aria/Aria.vue +9 -5
  7. package/dist/runtime/components/Focus.stories.d.ts +11 -0
  8. package/dist/runtime/components/Focus.stories.js +53 -0
  9. package/dist/runtime/components/Icon/Icon.vue +30 -10
  10. package/dist/runtime/components/LibButton/LibButton.stories.d.ts +12 -0
  11. package/dist/runtime/components/LibButton/LibButton.stories.js +94 -0
  12. package/dist/runtime/components/LibButton/LibButton.vue +72 -58
  13. package/dist/runtime/components/LibCheckbox/LibCheckbox.stories.d.ts +14 -0
  14. package/dist/runtime/components/LibCheckbox/LibCheckbox.stories.js +29 -0
  15. package/dist/runtime/components/LibCheckbox/LibCheckbox.vue +74 -48
  16. package/dist/runtime/components/LibColorInput/LibColorInput.stories.d.ts +7 -0
  17. package/dist/runtime/components/LibColorInput/LibColorInput.stories.js +58 -0
  18. package/dist/runtime/components/LibColorInput/LibColorInput.vue +107 -63
  19. package/dist/runtime/components/LibColorPicker/LibColorPicker.stories.d.ts +9 -0
  20. package/dist/runtime/components/LibColorPicker/LibColorPicker.stories.js +68 -0
  21. package/dist/runtime/components/LibColorPicker/LibColorPicker.vue +352 -271
  22. package/dist/runtime/components/LibDarkModeSwitcher/LibDarkModeSwitcher.stories.d.ts +7 -0
  23. package/dist/runtime/components/LibDarkModeSwitcher/LibDarkModeSwitcher.stories.js +36 -0
  24. package/dist/runtime/components/LibDarkModeSwitcher/LibDarkModeSwitcher.vue +56 -32
  25. package/dist/runtime/components/LibDatePicker/LibDatePicker.stories.d.ts +11 -0
  26. package/dist/runtime/components/LibDatePicker/LibDatePicker.stories.js +98 -0
  27. package/dist/runtime/components/LibDatePicker/LibDatePicker.vue +38 -17
  28. package/dist/runtime/components/LibDatePicker/LibRangeDatePicker.vue +82 -53
  29. package/dist/runtime/components/LibDatePicker/LibSingleDatePicker.vue +67 -50
  30. package/dist/runtime/components/LibDatePicker/LibTimeZonePicker.vue +8 -7
  31. package/dist/runtime/components/LibDebug/LibDebug.stories.d.ts +9 -0
  32. package/dist/runtime/components/LibDebug/LibDebug.stories.js +46 -0
  33. package/dist/runtime/components/LibDebug/LibDebug.vue +70 -42
  34. package/dist/runtime/components/LibDevOnly/LibDevOnly.vue +31 -18
  35. package/dist/runtime/components/LibFileInput/LibFileInput.stories.d.ts +10 -0
  36. package/dist/runtime/components/LibFileInput/LibFileInput.stories.js +63 -0
  37. package/dist/runtime/components/LibFileInput/LibFileInput.vue +156 -113
  38. package/dist/runtime/components/LibInputDeprecated/LibInputDeprecated.stories.d.ts +33 -0
  39. package/dist/runtime/components/LibInputDeprecated/LibInputDeprecated.stories.js +384 -0
  40. package/dist/runtime/components/LibInputDeprecated/LibInputDeprecated.vue +241 -215
  41. package/dist/runtime/components/LibLabel/LibLabel.stories.d.ts +6 -0
  42. package/dist/runtime/components/LibLabel/LibLabel.stories.js +25 -0
  43. package/dist/runtime/components/LibLabel/LibLabel.vue +46 -30
  44. package/dist/runtime/components/LibMultiValues/LibMultiValues.stories.d.ts +23 -0
  45. package/dist/runtime/components/LibMultiValues/LibMultiValues.stories.js +61 -0
  46. package/dist/runtime/components/LibMultiValues/LibMultiValues.vue +58 -44
  47. package/dist/runtime/components/LibNotifications/LibNotification.stories.d.ts +15 -0
  48. package/dist/runtime/components/LibNotifications/LibNotification.stories.js +126 -0
  49. package/dist/runtime/components/LibNotifications/LibNotification.vue +48 -32
  50. package/dist/runtime/components/LibNotifications/LibNotifications.stories.d.ts +6 -0
  51. package/dist/runtime/components/LibNotifications/LibNotifications.stories.js +109 -0
  52. package/dist/runtime/components/LibNotifications/LibNotifications.vue +83 -63
  53. package/dist/runtime/components/LibPagination/LibPagination.stories.d.ts +6 -0
  54. package/dist/runtime/components/LibPagination/LibPagination.stories.js +40 -0
  55. package/dist/runtime/components/LibPagination/LibPagination.vue +111 -67
  56. package/dist/runtime/components/LibPalette/LibPalette.stories.d.ts +6 -0
  57. package/dist/runtime/components/LibPalette/LibPalette.stories.js +20 -0
  58. package/dist/runtime/components/LibPalette/LibPalette.vue +23 -20
  59. package/dist/runtime/components/LibPopup/LibPopup.stories.d.ts +14 -0
  60. package/dist/runtime/components/LibPopup/LibPopup.stories.js +147 -0
  61. package/dist/runtime/components/LibPopup/LibPopup.vue +351 -314
  62. package/dist/runtime/components/LibProgressBar/LibProgressBar.stories.d.ts +10 -0
  63. package/dist/runtime/components/LibProgressBar/LibProgressBar.stories.js +81 -0
  64. package/dist/runtime/components/LibProgressBar/LibProgressBar.vue +91 -70
  65. package/dist/runtime/components/LibRecorder/LibRecorder.stories.d.ts +19 -0
  66. package/dist/runtime/components/LibRecorder/LibRecorder.stories.js +63 -0
  67. package/dist/runtime/components/LibRecorder/LibRecorder.vue +177 -133
  68. package/dist/runtime/components/LibRoot/LibRoot.vue +100 -73
  69. package/dist/runtime/components/LibSimpleInput/LibSimpleInput.stories.d.ts +26 -0
  70. package/dist/runtime/components/LibSimpleInput/LibSimpleInput.stories.js +78 -0
  71. package/dist/runtime/components/LibSimpleInput/LibSimpleInput.vue +77 -49
  72. package/dist/runtime/components/LibSuggestions/LibSuggestions.stories.d.ts +27 -0
  73. package/dist/runtime/components/LibSuggestions/LibSuggestions.stories.js +112 -0
  74. package/dist/runtime/components/LibSuggestions/LibSuggestions.vue +156 -123
  75. package/dist/runtime/components/LibTable/LibTable.stories.d.ts +16 -0
  76. package/dist/runtime/components/LibTable/LibTable.stories.js +156 -0
  77. package/dist/runtime/components/LibTable/LibTable.vue +99 -63
  78. package/dist/runtime/components/Reset.stories.d.ts +5 -0
  79. package/dist/runtime/components/Reset.stories.js +19 -0
  80. package/dist/runtime/components/Scrolling.stories.d.ts +6 -0
  81. package/dist/runtime/components/Scrolling.stories.js +44 -0
  82. package/dist/runtime/components/Template/NAME.vue +36 -15
  83. package/dist/runtime/components/TestControls/TestControls.vue +9 -6
  84. package/dist/runtime/composables/useScrollNearContainerEdges.stories.d.ts +7 -0
  85. package/dist/runtime/composables/useScrollNearContainerEdges.stories.js +85 -0
  86. package/dist/types.d.mts +6 -2
  87. package/dist/types.d.ts +7 -0
  88. package/package.json +11 -5
  89. package/src/module.ts +2 -1
  90. package/src/runtime/assets/utils.css +5 -5
  91. package/src/runtime/components/LibButton/LibButton.vue +2 -6
  92. package/src/runtime/nuxt/plugins/vue-plugin.ts +1 -1
  93. package/dist/runtime/components/Aria/Aria.vue.d.ts +0 -5
  94. package/dist/runtime/components/Icon/Icon.vue.d.ts +0 -21
  95. package/dist/runtime/components/LibButton/LibButton.vue.d.ts +0 -36
  96. package/dist/runtime/components/LibCheckbox/LibCheckbox.vue.d.ts +0 -42
  97. package/dist/runtime/components/LibColorInput/LibColorInput.vue.d.ts +0 -63
  98. package/dist/runtime/components/LibColorPicker/LibColorPicker.vue.d.ts +0 -61
  99. package/dist/runtime/components/LibDarkModeSwitcher/LibDarkModeSwitcher.vue.d.ts +0 -22
  100. package/dist/runtime/components/LibDatePicker/LibDatePicker.vue.d.ts +0 -40
  101. package/dist/runtime/components/LibDatePicker/LibRangeDatePicker.vue.d.ts +0 -34
  102. package/dist/runtime/components/LibDatePicker/LibSingleDatePicker.vue.d.ts +0 -34
  103. package/dist/runtime/components/LibDatePicker/LibTimeZonePicker.vue.d.ts +0 -22
  104. package/dist/runtime/components/LibDebug/LibDebug.vue.d.ts +0 -32
  105. package/dist/runtime/components/LibDevOnly/LibDevOnly.vue.d.ts +0 -22
  106. package/dist/runtime/components/LibFileInput/LibFileInput.vue.d.ts +0 -43
  107. package/dist/runtime/components/LibInputDeprecated/LibInputDeprecated.vue.d.ts +0 -165
  108. package/dist/runtime/components/LibLabel/LibLabel.vue.d.ts +0 -27
  109. package/dist/runtime/components/LibMultiValues/LibMultiValues.vue.d.ts +0 -29
  110. package/dist/runtime/components/LibNotifications/LibNotification.vue.d.ts +0 -17
  111. package/dist/runtime/components/LibNotifications/LibNotifications.vue.d.ts +0 -13
  112. package/dist/runtime/components/LibPagination/LibPagination.vue.d.ts +0 -104
  113. package/dist/runtime/components/LibPalette/LibPalette.vue.d.ts +0 -14
  114. package/dist/runtime/components/LibPopup/LibPopup.vue.d.ts +0 -46
  115. package/dist/runtime/components/LibProgressBar/LibProgressBar.vue.d.ts +0 -41
  116. package/dist/runtime/components/LibRecorder/LibRecorder.vue.d.ts +0 -77
  117. package/dist/runtime/components/LibRoot/LibRoot.vue.d.ts +0 -41
  118. package/dist/runtime/components/LibSimpleInput/LibSimpleInput.vue.d.ts +0 -35
  119. package/dist/runtime/components/LibSuggestions/LibSuggestions.vue.d.ts +0 -94
  120. package/dist/runtime/components/LibTable/LibTable.vue.d.ts +0 -45
  121. package/dist/runtime/components/Template/NAME.vue.d.ts +0 -17
  122. package/dist/runtime/components/TestControls/TestControls.vue.d.ts +0 -5
@@ -2,8 +2,7 @@
2
2
  <div
3
3
  :id="id ?? fallbackId"
4
4
  :aria-label="t('color-picker.aria')"
5
- :class="twMerge(
6
- `color-picker
5
+ :class="twMerge(`color-picker
7
6
  [--slider-size:calc(var(--spacing)_*_4)]
8
7
  [--contrast-dark:var(--color-neutral-100)]
9
8
  [--contrast-light:var(--color-neutral-800)]
@@ -16,15 +15,15 @@
16
15
  gap-3
17
16
  p-3
18
17
  `,
19
- invertColors && `
18
+ invertColors && `
20
19
  [--fg:rgb(var(--contrast-light))]
21
20
  [--bg:rgb(var(--contrast-dark))]
22
21
  `,
23
- border && `
22
+ border && `
24
23
  border rounded-sm border-neutral-600
25
24
  `,
26
- $attrs?.class
27
- )"
25
+ ($attrs as any)?.class
26
+ )"
28
27
  >
29
28
  <div
30
29
  :class="`color-picker--all-picker
@@ -169,29 +168,37 @@
169
168
  </div>
170
169
  </template>
171
170
 
172
- <script setup>
173
- import { castType } from "@alanscodelog/utils/castType.js";
174
- import { clampNumber } from "@alanscodelog/utils/clampNumber.js";
175
- import { isArray } from "@alanscodelog/utils/isArray.js";
176
- import { unreachable } from "@alanscodelog/utils/unreachable.js";
177
- import Color from "colorjs.io";
178
- import { computed, onMounted, reactive, ref, useAttrs, watch } from "vue";
179
- import { safeConvertToHsva } from "./utils/safeConvertToHsva.js";
180
- import { safeConvertToRgba } from "./utils/safeConvertToRgba.js";
181
- import { toLowPrecisionRgbaString } from "./utils/toLowPrecisionRgbaString.js";
182
- import { useInjectedI18n } from "../../composables/useInjectedI18n.js";
183
- import { copy } from "../../helpers/copy.js";
184
- import { twMerge } from "../../utils/twMerge.js";
185
- import aria from "../Aria/Aria.vue";
186
- import Icon from "../Icon/Icon.vue";
187
- import LibButton from "../LibButton/LibButton.vue";
188
- import LibSimpleInput from "../LibSimpleInput/LibSimpleInput.vue";
189
- import { getFallbackId } from "../shared/props.js";
171
+ <script setup lang="ts">
172
+ /* todo change to colorjsio for less dependencies */
173
+ import { castType } from "@alanscodelog/utils/castType.js"
174
+ import { clampNumber } from "@alanscodelog/utils/clampNumber.js"
175
+ import { isArray } from "@alanscodelog/utils/isArray.js"
176
+ import { unreachable } from "@alanscodelog/utils/unreachable.js"
177
+ import Color from "colorjs.io"
178
+ import { computed, onMounted, reactive, type Ref, ref, type UnwrapRef,useAttrs, watch } from "vue"
179
+
180
+ import { safeConvertToHsva } from "./utils/safeConvertToHsva.js"
181
+ import { safeConvertToRgba } from "./utils/safeConvertToRgba.js"
182
+ import { toLowPrecisionRgbaString } from "./utils/toLowPrecisionRgbaString.js"
183
+
184
+ import { useInjectedI18n } from "../../composables/useInjectedI18n.js"
185
+ import { copy } from "../../helpers/copy.js"
186
+ import type { HsvaColor, RgbaColor } from "../../types/index.js"
187
+ import { twMerge } from "../../utils/twMerge.js"
188
+ import aria from "../Aria/Aria.vue"
189
+ import Icon from "../Icon/Icon.vue"
190
+ import LibButton from "../LibButton/LibButton.vue"
191
+ import LibSimpleInput from "../LibSimpleInput/LibSimpleInput.vue"
192
+ import { getFallbackId, type LabelProps , type LinkableByIdProps } from "../shared/props.js"
193
+
190
194
  defineOptions({
191
- name: "lib-color-picker"
192
- });
193
- const $attrs = useAttrs();
194
- const t = useInjectedI18n();
195
+ name: "lib-color-picker",
196
+ })
197
+
198
+ const $attrs = useAttrs()
199
+
200
+ const t = useInjectedI18n()
201
+
195
202
  const sliderClasses = `
196
203
  slider
197
204
  no-touch-action
@@ -199,7 +206,8 @@ const sliderClasses = `
199
206
  w-full
200
207
  relative
201
208
  flex
202
- `;
209
+ `
210
+
203
211
  const handleClasses = `
204
212
  handle
205
213
  h-[var(--slider-size)]
@@ -214,265 +222,338 @@ const handleClasses = `
214
222
  focus:border-accent-500
215
223
  active:border-accent-500
216
224
  hover:border-accent-500
217
- `;
218
- const emits = defineEmits(["save", "cancel"]);
219
- const props = defineProps({
220
- label: { type: String, required: false },
221
- id: { type: String, required: false },
222
- allowAlpha: { type: Boolean, required: false, default: true },
223
- stringPrecision: { type: Number, required: false, default: 3 },
224
- customRepresentation: { type: Object, required: false, default: void 0 },
225
- border: { type: Boolean, required: false, default: true },
226
- copyTransform: { type: Function, required: false, default: (_val, stringVal) => stringVal },
227
- valid: { type: Boolean, required: false, default: true }
228
- });
229
- const ariaDescription = t("color-picker.aria.description");
230
- const fallbackId = getFallbackId();
231
- const $value = defineModel({ type: Object, ...{ required: false, default: () => ({ r: 0, g: 0, b: 0 }) } });
232
- const $tempValue = defineModel("tempValue", { type: null, ...{ required: false, default: () => void 0 } });
233
- const pickerEl = ref(null);
234
- const hueSliderEl = ref(null);
235
- const alphaSliderEl = ref(null);
236
- const config = {
237
- hue: {
238
- el: hueSliderEl,
239
- xKey: "h",
240
- xSteps: 360
241
- },
242
- alpha: {
243
- el: alphaSliderEl,
244
- xSteps: 1,
245
- xKey: "a"
246
- },
247
- all: {
248
- el: pickerEl,
249
- xSteps: 100,
250
- ySteps: 100,
251
- xKey: "s",
252
- yKey: "v"
253
- }
254
- };
255
- const localColor = reactive({
256
- percent: {
257
- h: 0,
258
- s: 0,
259
- v: 0,
260
- a: 0
261
- },
262
- val: {
263
- h: 0,
264
- s: 0,
265
- v: 0,
266
- a: 0
267
- }
268
- });
225
+ `
226
+ const emits = defineEmits<{
227
+ (e: "save", val: RgbaColor): void
228
+ (e: "cancel"): void
229
+ }>()
230
+
231
+ const props = withDefaults(defineProps<
232
+ LabelProps
233
+ & LinkableByIdProps
234
+ & {
235
+ allowAlpha?: boolean
236
+ /**
237
+ * The precision of the rgba string representation of the color. Defaults to 3. Extra trailing zeros are removed for a prettier number.
238
+ *
239
+ * Does not affect the number saved unless the user manually edits the color.
240
+ *
241
+ * Ignored if `customRepresentation` is set.
242
+ */
243
+ stringPrecision?: number
244
+ /** Allows overriding the string representation of the color. Useful for using a different representation than rgba (e.g. hex). The fromStringToHsva part is rarely needed as the colorjs.io library can normally parse the color. Returning undefined signals an error. */
245
+ customRepresentation?: {
246
+ fromHsvaToString: (hsva: HsvaColor, includeAlpha: boolean) => string
247
+ fromStringToHsva?: (string: string) => HsvaColor | undefined
248
+ }
249
+ border?: boolean
250
+ /** Modify what the user copies to the clipboard. */
251
+ copyTransform?: (val: HsvaColor, stringVal: string) => any
252
+ valid?: boolean
253
+ }>(), {
254
+ allowAlpha: true,
255
+ border: true,
256
+ stringPrecision: 3,
257
+ copyTransform: (_val: HsvaColor, stringVal: string) => stringVal,
258
+ customRepresentation: undefined,
259
+ valid: true,
260
+ })
261
+
262
+
263
+ const ariaDescription = t("color-picker.aria.description")
264
+ const fallbackId = getFallbackId()
265
+
266
+ const $value = defineModel<RgbaColor>({ required: false, default: () => ({ r: 0, g: 0, b: 0 }) })
267
+ const $tempValue = defineModel<RgbaColor | undefined>("tempValue", { required: false, default: () => (undefined) })
268
+
269
+ const pickerEl = ref<HTMLCanvasElement | null>(null)
270
+ const hueSliderEl = ref<HTMLCanvasElement | null>(null)
271
+ const alphaSliderEl = ref<HTMLCanvasElement | null>(null)
272
+
273
+ type Config = Record<string, {
274
+ el: Ref<HTMLCanvasElement>
275
+ xKey?: keyof HsvaColor
276
+ yKey?: keyof HsvaColor
277
+ xSteps?: number
278
+ ySteps?: number
279
+ }>
280
+
281
+ const config: Config = {
282
+ hue: {
283
+ el: hueSliderEl as Ref<HTMLCanvasElement>,
284
+ xKey: "h",
285
+ xSteps: 360,
286
+ },
287
+ alpha: {
288
+ el: alphaSliderEl as Ref<HTMLCanvasElement>,
289
+ xSteps: 1,
290
+ xKey: "a",
291
+ },
292
+ all: {
293
+ el: pickerEl as Ref<HTMLCanvasElement>,
294
+ xSteps: 100,
295
+ ySteps: 100,
296
+ xKey: "s",
297
+ yKey: "v",
298
+ },
299
+ }
300
+
301
+ const localColor = reactive<Record<"percent" | "val", HsvaColor>>({
302
+ percent: {
303
+ h: 0, s: 0, v: 0, a: 0,
304
+ },
305
+ val: {
306
+ h: 0, s: 0, v: 0, a: 0,
307
+ },
308
+ })
309
+
310
+
269
311
  const asRgba = computed(() => {
270
- const rgba = safeConvertToRgba(localColor.val, props.allowAlpha);
271
- if (!rgba) unreachable();
272
- return rgba;
273
- });
312
+ const rgba = safeConvertToRgba(localColor.val, props.allowAlpha)
313
+ if (!rgba) unreachable()
314
+ return rgba
315
+ })
274
316
  const asRgbaString = computed(() => {
275
- const rgba = asRgba.value;
276
- if (!rgba) unreachable();
277
- return `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${rgba.a})`;
278
- });
317
+ const rgba = asRgba.value
318
+ if (!rgba) unreachable()
319
+ return `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${rgba.a})`
320
+ })
279
321
  const localColorString = computed(() => {
280
- if (props.customRepresentation) {
281
- return props.customRepresentation.fromHsvaToString({ ...localColor.val }, props.allowAlpha);
282
- }
283
- return toLowPrecisionRgbaString(asRgba.value, props.allowAlpha, props.stringPrecision);
284
- });
285
- const localInputString = ref(localColorString.value);
322
+ if (props.customRepresentation) {
323
+ return props.customRepresentation.fromHsvaToString({ ...localColor.val }, props.allowAlpha)
324
+ }
325
+ return toLowPrecisionRgbaString(asRgba.value, props.allowAlpha, props.stringPrecision)
326
+ })
327
+
328
+ const localInputString = ref(localColorString.value)
329
+ // fixes the localInputString not updating when the user inputs an invalid value
286
330
  function onBlurFixInvalidValue() {
287
- if (localInputString.value !== localColorString.value) {
288
- localInputString.value = localColorString.value;
289
- }
331
+ if (localInputString.value !== localColorString.value) {
332
+ localInputString.value = localColorString.value
333
+ }
290
334
  }
291
- function updatePicker(el, hue) {
292
- const ctx = el.getContext("2d");
293
- const { height, width } = el;
294
- ctx.clearRect(0, 0, width, height);
295
- const gradient = ctx.createLinearGradient(0, 0, 0, height);
296
- gradient.addColorStop(0, "white");
297
- gradient.addColorStop(1, "black");
298
- const gradientColor = ctx.createLinearGradient(0, 0, width, 0);
299
- gradientColor.addColorStop(0, `hsla(${hue} 100% 50% / 0)`);
300
- gradientColor.addColorStop(1, `hsla(${hue} 100% 50% /1)`);
301
- ctx.fillStyle = gradient;
302
- ctx.fillRect(0, 0, width, height);
303
- ctx.fillStyle = gradientColor;
304
- ctx.globalCompositeOperation = "multiply";
305
- ctx.fillRect(0, 0, width, height);
306
- ctx.globalCompositeOperation = "source-over";
335
+
336
+ function updatePicker(el: HTMLCanvasElement, hue: number): void {
337
+ const ctx = el.getContext("2d")!
338
+ const { height, width } = el
339
+ ctx.clearRect(0, 0, width, height)
340
+
341
+ const gradient = ctx.createLinearGradient(0, 0, 0, height)
342
+ gradient.addColorStop(0, "white")
343
+ gradient.addColorStop(1, "black")
344
+ const gradientColor = ctx.createLinearGradient(0, 0, width, 0)
345
+ gradientColor.addColorStop(0, `hsla(${hue} 100% 50% / 0)`)
346
+ gradientColor.addColorStop(1, `hsla(${hue} 100% 50% /1)`)
347
+
348
+
349
+ ctx.fillStyle = gradient
350
+ ctx.fillRect(0, 0, width, height)
351
+ ctx.fillStyle = gradientColor
352
+ ctx.globalCompositeOperation = "multiply"
353
+ ctx.fillRect(0, 0, width, height)
354
+ ctx.globalCompositeOperation = "source-over"
307
355
  }
308
- function updateSlider(el, stops, length = 360) {
309
- const ctx = el.getContext("2d");
310
- const { height, width } = el;
311
- ctx.clearRect(0, 0, width, height);
312
- const end = isArray(stops) ? stops.length - 1 : length;
313
- const gradient = ctx.createLinearGradient(0, 0, width, 0);
314
- for (let i = 0; i < end + 1; i++) {
315
- const stop = stops instanceof Function ? stops(i) : stops[i];
316
- if (stop === void 0) unreachable();
317
- gradient.addColorStop(i / end, stop);
318
- }
319
- ctx.fillStyle = gradient;
320
- ctx.fillRect(0, 0, width, height);
356
+
357
+ function updateSlider(el: HTMLCanvasElement, stops: ((i: number) => string) | string[], length: number = 360): void {
358
+ const ctx = el.getContext("2d")!
359
+ const { height, width } = el
360
+ ctx.clearRect(0, 0, width, height)
361
+
362
+ const end = isArray(stops) ? stops.length - 1 : length
363
+
364
+ const gradient = ctx.createLinearGradient(0, 0, width, 0)
365
+
366
+ for (let i = 0; i < end + 1; i++) {
367
+ const stop = stops instanceof Function ? stops(i) : stops[i]
368
+ if (stop === undefined) unreachable()
369
+ gradient.addColorStop(i / end, stop)
370
+ }
371
+
372
+ ctx.fillStyle = gradient
373
+ ctx.fillRect(0, 0, width, height)
321
374
  }
322
- function getVal(x, width, steps = 100, accuracy = 100, reverse = false) {
323
- const percent = x / width;
324
- const unrounded = percent * steps;
325
- const val = Math.round(unrounded * accuracy) / accuracy;
326
- const percentVal = steps === accuracy ? val : Math.round(percent * 100 * accuracy) / accuracy;
327
- const res = { val, percent: percentVal };
328
- if (reverse) res.val = steps - val;
329
- return res;
375
+
376
+
377
+ function getVal(x: number, width: number, steps: number = 100, accuracy: number = 100, reverse = false): { val: number, percent: number } {
378
+ const percent = (x / width)
379
+ const unrounded = percent * steps
380
+
381
+ const val = Math.round(unrounded * accuracy) / accuracy
382
+ const percentVal = steps === accuracy ? val : Math.round(percent * 100 * accuracy) / accuracy
383
+ const res = { val, percent: percentVal }
384
+ if (reverse) res.val = steps - val
385
+ return res
330
386
  }
331
- const elDragging = ref("");
332
- let dragging = false;
333
- function moveHandle(e, type) {
334
- requestAnimationFrame(() => {
335
- if (type === "") return;
336
- const el = config[type]?.el.value;
337
- if (!el || !config[type]) return;
338
- const { x, y, width, height } = el.getBoundingClientRect();
339
- const info = config[type];
340
- if (info.xKey !== void 0) {
341
- let newPosX = e.clientX - x;
342
- newPosX = newPosX < 0 ? 0 : newPosX > width ? width : newPosX;
343
- const newX = getVal(newPosX, width, info.xSteps ?? 100);
344
- localColor.percent[info.xKey] = newX.percent;
345
- localColor.val[info.xKey] = newX.val;
346
- }
347
- if (info.yKey !== void 0) {
348
- let newPosY = e.clientY - y;
349
- newPosY = newPosY < 0 ? 0 : newPosY > height ? height : newPosY;
350
- const newY = getVal(newPosY, height, info.ySteps ?? 100, 100, true);
351
- localColor.percent[info.yKey] = newY.percent;
352
- localColor.val[info.yKey] = newY.val;
353
- }
354
- });
387
+
388
+ type Types = keyof UnwrapRef<Config>
389
+ const elDragging = ref<Types | "">("")
390
+ let dragging = false
391
+
392
+ function moveHandle(e: { clientX: number, clientY: number }, type: string) {
393
+ requestAnimationFrame(() => {
394
+ if (type === "") return
395
+ const el = config[type]?.el.value
396
+ if (!el || !config[type]) return
397
+ const { x, y, width, height } = el.getBoundingClientRect()
398
+
399
+ const info = config[type]
400
+ if (info.xKey !== undefined) {
401
+ let newPosX = e.clientX - x
402
+ newPosX = newPosX < 0 ? 0 : newPosX > width ? width : newPosX
403
+ const newX = getVal(newPosX, width, info.xSteps ?? 100)
404
+
405
+ localColor.percent[info.xKey] = newX.percent
406
+ localColor.val[info.xKey] = newX.val
407
+ }
408
+
409
+ if (info.yKey !== undefined) {
410
+ let newPosY = e.clientY - y
411
+ newPosY = newPosY < 0 ? 0 : newPosY > height ? height : newPosY
412
+ const newY = getVal(newPosY, height, info.ySteps ?? 100, 100, true)
413
+
414
+ localColor.percent[info.yKey] = newY.percent
415
+ localColor.val[info.yKey] = newY.val
416
+ }
417
+ })
355
418
  }
356
419
  const slider = {
357
- keydown: (e, type) => {
358
- castType(e.target);
359
- if (e.target?.getBoundingClientRect) {
360
- if (["ArrowRight", "ArrowLeft", "ArrowUp", "ArrowDown"].includes(e.key)) {
361
- e.preventDefault();
362
- const { x, y, width, height } = e.target.getBoundingClientRect();
363
- let xDiff = e.key === "ArrowRight" ? 1 : e.key === "ArrowLeft" ? -1 : 0;
364
- let yDiff = e.key === "ArrowUp" ? -1 : e.key === "ArrowDown" ? 1 : 0;
365
- if (e.shiftKey) {
366
- xDiff *= 10;
367
- }
368
- if (e.shiftKey) {
369
- yDiff *= 10;
370
- }
371
- moveHandle({ clientX: x + width / 2 + xDiff, clientY: y + height / 2 + yDiff }, type);
372
- }
373
- if (e.key === "Enter") {
374
- e.preventDefault();
375
- save();
376
- }
377
- }
378
- },
379
- pointerdown: (e, type) => {
380
- const focusTargetClass = `#${props.id ?? fallbackId} .color-picker--${type}-handle`;
381
- const focusTarget = document.querySelector(focusTargetClass);
382
- if (focusTarget instanceof HTMLElement) focusTarget.focus();
383
- if (dragging) return;
384
- e.preventDefault();
385
- elDragging.value = type;
386
- dragging = true;
387
- document.addEventListener("pointermove", slider.pointermove);
388
- document.addEventListener("pointerup", slider.pointerup);
389
- moveHandle(e, elDragging.value);
390
- },
391
- pointerleave: (e) => {
392
- if (dragging) {
393
- e.preventDefault();
394
- }
395
- },
396
- pointermove: (e) => {
397
- e.preventDefault();
398
- moveHandle(e, elDragging.value);
399
- },
400
- pointerup: (e) => {
401
- e.preventDefault();
402
- dragging = false;
403
- elDragging.value = "";
404
- document.removeEventListener("pointermove", slider.pointermove);
405
- document.removeEventListener("pointerup", slider.pointerup);
406
- }
407
- };
408
- function updateSliders(_) {
409
- if (alphaSliderEl.value) {
410
- const color = new Color("hsv", [_.h, _.s, _.v], _.a).to("hsl");
411
- const hsl0 = color.clone();
412
- hsl0.alpha = 0;
413
- const hsl1 = color.clone();
414
- hsl1.alpha = 1;
415
- updateSlider(alphaSliderEl.value, [hsl0.toString(), hsl1.toString()]);
416
- }
417
- updateSlider(hueSliderEl.value, (i) => `hsl(${i} 100% 50%)`);
418
- updatePicker(pickerEl.value, _.h);
420
+ keydown: (e: KeyboardEvent, type: Types) => {
421
+ castType<HTMLElement | undefined>(e.target)
422
+ if (e.target?.getBoundingClientRect) {
423
+ if (["ArrowRight", "ArrowLeft", "ArrowUp", "ArrowDown"].includes(e.key)) {
424
+ e.preventDefault()
425
+ const { x, y, width, height } = e.target.getBoundingClientRect()
426
+ let xDiff = e.key === "ArrowRight" ? 1 : e.key === "ArrowLeft" ? -1 : 0
427
+ let yDiff = e.key === "ArrowUp" ? -1 : e.key === "ArrowDown" ? 1 : 0
428
+ if (e.shiftKey) {xDiff *= 10}
429
+ if (e.shiftKey) {yDiff *= 10}
430
+ moveHandle({ clientX: x + (width / 2) + xDiff, clientY: y + (height / 2) + yDiff }, type)
431
+ }
432
+ if (e.key === "Enter") {
433
+ e.preventDefault()
434
+ save()
435
+ }
436
+ }
437
+ },
438
+ pointerdown: (e: PointerEvent, type: Types) => {
439
+ const focusTargetClass = `#${props.id ?? fallbackId} .color-picker--${type}-handle`
440
+ const focusTarget = document.querySelector(focusTargetClass)
441
+ // allows enter to work when the user drags any slider as the even will be captured by the keydown listener
442
+ if (focusTarget instanceof HTMLElement) focusTarget.focus()
443
+
444
+ if (dragging) return
445
+ e.preventDefault()
446
+ elDragging.value = type
447
+ dragging = true
448
+ document.addEventListener("pointermove", slider.pointermove)
449
+ document.addEventListener("pointerup", slider.pointerup)
450
+ moveHandle(e, elDragging.value)
451
+ },
452
+ pointerleave: (e: PointerEvent) => {
453
+ if (dragging) {
454
+ e.preventDefault()
455
+ }
456
+ },
457
+ pointermove: (e: PointerEvent) => {
458
+ e.preventDefault()
459
+ moveHandle(e, elDragging.value)
460
+ },
461
+ pointerup: (e: PointerEvent) => {
462
+ e.preventDefault()
463
+ dragging = false
464
+ elDragging.value = ""
465
+ document.removeEventListener("pointermove", slider.pointermove)
466
+ document.removeEventListener("pointerup", slider.pointerup)
467
+ },
468
+ }
469
+ function updateSliders(_: HsvaColor): void {
470
+ if (alphaSliderEl.value) {
471
+ // https://colorjs.io/docs/output#get-a-displayable-css-color-value
472
+ const color = new Color("hsv", [_.h, _.s, _.v], _.a).to("hsl")
473
+ const hsl0 = color.clone()
474
+ hsl0.alpha = 0
475
+ const hsl1 = color.clone()
476
+ hsl1.alpha = 1
477
+ updateSlider(alphaSliderEl.value, [hsl0.toString(), hsl1.toString()])
478
+ }
479
+ updateSlider(hueSliderEl.value!, i => `hsl(${i} 100% 50%)`)
480
+ updatePicker(pickerEl.value!, _.h)
419
481
  }
420
- function updateValueAndPosition(_) {
421
- localColor.percent.h = Math.round(_.h / 360 * 1e4) / 100;
422
- localColor.percent.s = _.s;
423
- localColor.percent.v = 100 - _.v;
424
- localColor.percent.a = props.allowAlpha ? _.a !== void 0 ? _.a * 100 : 1 : 1;
425
- localColor.val = { ..._, a: props.allowAlpha ? _.a : 1 };
482
+
483
+ function updateValueAndPosition(_: HsvaColor): void {
484
+ localColor.percent.h = Math.round((_.h / 360) * 10000) / 100
485
+ localColor.percent.s = _.s
486
+ localColor.percent.v = 100 - _.v
487
+ localColor.percent.a =
488
+ props.allowAlpha
489
+ ? _.a !== undefined
490
+ ? _.a * 100
491
+ : 1
492
+ : 1
493
+ localColor.val = { ..._, a: props.allowAlpha ? _.a : 1 }
426
494
  }
427
- function convertAndUpdateAll(rgba) {
428
- const hsva = safeConvertToHsva(rgba, props.allowAlpha);
429
- if (!hsva) return;
430
- updateSliders(hsva);
431
- updateValueAndPosition(hsva);
495
+
496
+ function convertAndUpdateAll(rgba: RgbaColor) {
497
+ const hsva = safeConvertToHsva(rgba, props.allowAlpha)
498
+ if (!hsva) return
499
+ updateSliders(hsva)
500
+ updateValueAndPosition(hsva)
432
501
  }
433
- function save() {
434
- const rgba = safeConvertToRgba(localColor.val, props.allowAlpha);
435
- if (!rgba) return;
436
- $value.value = rgba;
437
- $tempValue.value = void 0;
438
- emits("save", rgba);
502
+
503
+ function save(): void {
504
+ const rgba = safeConvertToRgba(localColor.val, props.allowAlpha)
505
+ if (!rgba) return
506
+ // update(localColor.val, { updatePosition: false, updateValue: false })
507
+ $value.value = rgba
508
+ $tempValue.value = undefined
509
+ emits("save", rgba)
439
510
  }
440
- function parseInput(e) {
441
- const val = e.target?.value;
442
- const converted = props.customRepresentation?.fromStringToHsva ? props.customRepresentation.fromStringToHsva(val) : safeConvertToHsva(val, props.allowAlpha);
443
- if (converted) {
444
- updateSliders(converted);
445
- updateValueAndPosition(converted);
446
- }
511
+
512
+
513
+ function parseInput(e: Event): void {
514
+ const val = (e.target as HTMLInputElement)?.value
515
+ const converted = props.customRepresentation?.fromStringToHsva
516
+ ? props.customRepresentation.fromStringToHsva(val)
517
+ : safeConvertToHsva(val, props.allowAlpha)
518
+ if (converted) {
519
+ updateSliders(converted)
520
+ updateValueAndPosition(converted)
521
+ }
447
522
  }
448
- let disableUpdateTempValue = false;
523
+
524
+
525
+ let disableUpdateTempValue = false
526
+
449
527
  onMounted(() => {
450
- convertAndUpdateAll($value.value);
451
- if ($tempValue.value !== void 0) {
452
- convertAndUpdateAll($tempValue.value);
453
- }
454
- const handle = document.querySelector(`#${props.id ?? fallbackId} .color-picker--all-handle`);
455
- if (handle instanceof HTMLElement) handle.focus();
456
- });
528
+ convertAndUpdateAll($value.value)
529
+ if ($tempValue.value !== undefined) {
530
+ convertAndUpdateAll($tempValue.value)
531
+ }
532
+
533
+ const handle = document.querySelector(`#${props.id ?? fallbackId} .color-picker--all-handle`)
534
+ if (handle instanceof HTMLElement) handle.focus()
535
+ })
536
+
457
537
  watch($value, () => {
458
- convertAndUpdateAll($value.value);
459
- });
538
+ convertAndUpdateAll($value.value)
539
+ })
540
+
460
541
  watch($tempValue, () => {
461
- if ($tempValue.value !== void 0) {
462
- disableUpdateTempValue = true;
463
- convertAndUpdateAll($tempValue.value);
464
- setTimeout(() => {
465
- disableUpdateTempValue = false;
466
- }, 0);
467
- }
468
- });
542
+ if ($tempValue.value !== undefined) {
543
+ disableUpdateTempValue = true
544
+ convertAndUpdateAll($tempValue.value)
545
+ setTimeout(() => { disableUpdateTempValue = false }, 0)
546
+ }
547
+ })
548
+
469
549
  watch(localColor, () => {
470
- updateSliders(localColor.val);
471
- localInputString.value = localColorString.value;
472
- if (disableUpdateTempValue) return;
473
- const rgba = safeConvertToRgba(localColor.val, props.allowAlpha);
474
- if (!rgba) return;
475
- $tempValue.value = rgba;
476
- });
477
- const invertColors = computed(() => !!(localColor.percent.v < 50 || (localColor.val.a === void 0 || localColor.val.a < 0.5)));
550
+ updateSliders(localColor.val)
551
+ localInputString.value = localColorString.value
552
+ if (disableUpdateTempValue) return
553
+ const rgba = safeConvertToRgba(localColor.val, props.allowAlpha)
554
+ if (!rgba) return
555
+ $tempValue.value = rgba
556
+ })
557
+
558
+ const invertColors = computed(() => !!(localColor.percent.v < 50 || (localColor.val.a === undefined || localColor.val.a < 0.5)))
478
559
  </script>