@witchcraft/ui 0.0.1 → 0.1.1

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 (224) hide show
  1. package/README.md +18 -28
  2. package/dist/module.d.mts +3 -1
  3. package/dist/module.json +3 -3
  4. package/dist/module.mjs +21 -12
  5. package/dist/runtime/assets/base.css +1 -1
  6. package/dist/runtime/assets/locales/en.json +2 -2
  7. package/dist/runtime/assets/tailwind.css +1 -1
  8. package/dist/runtime/assets/utils.css +1 -0
  9. package/dist/runtime/build/WitchcraftUiResolver.js +1 -1
  10. package/dist/runtime/components/Aria/Aria.vue +5 -9
  11. package/dist/runtime/components/Aria/Aria.vue.d.ts +5 -0
  12. package/dist/runtime/components/Icon/Icon.vue +12 -28
  13. package/dist/runtime/components/Icon/Icon.vue.d.ts +21 -0
  14. package/dist/runtime/components/LibButton/LibButton.vue +93 -117
  15. package/dist/runtime/components/LibButton/LibButton.vue.d.ts +36 -0
  16. package/dist/runtime/components/LibCheckbox/LibCheckbox.vue +53 -76
  17. package/dist/runtime/components/LibCheckbox/LibCheckbox.vue.d.ts +42 -0
  18. package/dist/runtime/components/LibColorInput/LibColorInput.vue +131 -101
  19. package/dist/runtime/components/LibColorInput/LibColorInput.vue.d.ts +63 -0
  20. package/dist/runtime/components/LibColorPicker/LibColorPicker.vue +326 -296
  21. package/dist/runtime/components/LibColorPicker/LibColorPicker.vue.d.ts +61 -0
  22. package/dist/runtime/components/LibColorPicker/utils/safeConvertToHsva.d.ts +2 -0
  23. package/dist/runtime/components/LibColorPicker/utils/safeConvertToHsva.js +18 -0
  24. package/dist/runtime/components/LibColorPicker/utils/safeConvertToRgba.d.ts +2 -0
  25. package/dist/runtime/components/LibColorPicker/utils/safeConvertToRgba.js +17 -0
  26. package/dist/runtime/components/LibColorPicker/utils/toLowPrecisionRgbaString.d.ts +2 -0
  27. package/dist/runtime/components/LibColorPicker/utils/toLowPrecisionRgbaString.js +8 -0
  28. package/dist/runtime/components/LibColorPicker/utils/truncate.d.ts +1 -0
  29. package/dist/runtime/components/LibColorPicker/utils/truncate.js +5 -0
  30. package/dist/runtime/components/LibDarkModeSwitcher/LibDarkModeSwitcher.vue +42 -64
  31. package/dist/runtime/components/LibDarkModeSwitcher/LibDarkModeSwitcher.vue.d.ts +22 -0
  32. package/dist/runtime/components/LibDatePicker/LibDatePicker.vue +20 -54
  33. package/dist/runtime/components/LibDatePicker/LibDatePicker.vue.d.ts +40 -0
  34. package/dist/runtime/components/LibDatePicker/LibRangeDatePicker.vue +205 -173
  35. package/dist/runtime/components/LibDatePicker/LibRangeDatePicker.vue.d.ts +34 -0
  36. package/dist/runtime/components/LibDatePicker/LibSingleDatePicker.vue +215 -164
  37. package/dist/runtime/components/LibDatePicker/LibSingleDatePicker.vue.d.ts +34 -0
  38. package/dist/runtime/components/LibDatePicker/LibTimeZonePicker.vue +9 -10
  39. package/dist/runtime/components/LibDatePicker/LibTimeZonePicker.vue.d.ts +22 -0
  40. package/dist/runtime/components/LibDebug/LibDebug.vue +47 -65
  41. package/dist/runtime/components/LibDebug/LibDebug.vue.d.ts +32 -0
  42. package/dist/runtime/components/LibDevOnly/LibDevOnly.vue +19 -34
  43. package/dist/runtime/components/LibDevOnly/LibDevOnly.vue.d.ts +22 -0
  44. package/dist/runtime/components/LibFileInput/LibFileInput.vue +155 -173
  45. package/dist/runtime/components/LibFileInput/LibFileInput.vue.d.ts +43 -0
  46. package/dist/runtime/components/LibInputDeprecated/LibInputDeprecated.vue +352 -0
  47. package/dist/runtime/components/LibInputDeprecated/LibInputDeprecated.vue.d.ts +165 -0
  48. package/dist/runtime/components/LibLabel/LibLabel.vue +30 -46
  49. package/dist/runtime/components/LibLabel/LibLabel.vue.d.ts +27 -0
  50. package/dist/runtime/components/LibMultiValues/LibMultiValues.vue +50 -66
  51. package/dist/runtime/components/LibMultiValues/LibMultiValues.vue.d.ts +29 -0
  52. package/dist/runtime/components/LibNotifications/LibNotification.vue +48 -56
  53. package/dist/runtime/components/LibNotifications/LibNotification.vue.d.ts +17 -0
  54. package/dist/runtime/components/LibNotifications/LibNotifications.vue +71 -83
  55. package/dist/runtime/components/LibNotifications/LibNotifications.vue.d.ts +13 -0
  56. package/dist/runtime/components/LibPagination/LibPagination.vue +86 -131
  57. package/dist/runtime/components/LibPagination/LibPagination.vue.d.ts +104 -0
  58. package/dist/runtime/components/LibPalette/LibPalette.vue +23 -26
  59. package/dist/runtime/components/LibPalette/LibPalette.vue.d.ts +14 -0
  60. package/dist/runtime/components/LibPopup/LibPopup.vue +326 -400
  61. package/dist/runtime/components/LibPopup/LibPopup.vue.d.ts +46 -0
  62. package/dist/runtime/components/LibProgressBar/LibProgressBar.vue +73 -93
  63. package/dist/runtime/components/LibProgressBar/LibProgressBar.vue.d.ts +41 -0
  64. package/dist/runtime/components/LibRecorder/LibRecorder.vue +134 -179
  65. package/dist/runtime/components/LibRecorder/LibRecorder.vue.d.ts +77 -0
  66. package/dist/runtime/components/LibRoot/LibRoot.vue +75 -89
  67. package/dist/runtime/components/LibRoot/LibRoot.vue.d.ts +41 -0
  68. package/dist/runtime/components/LibSimpleInput/LibSimpleInput.vue +51 -82
  69. package/dist/runtime/components/LibSimpleInput/LibSimpleInput.vue.d.ts +35 -0
  70. package/dist/runtime/components/LibSuggestions/LibSuggestions.vue +147 -164
  71. package/dist/runtime/components/LibSuggestions/LibSuggestions.vue.d.ts +94 -0
  72. package/dist/runtime/components/LibTable/LibTable.vue +69 -106
  73. package/dist/runtime/components/LibTable/LibTable.vue.d.ts +45 -0
  74. package/dist/runtime/components/Template/NAME.vue +15 -36
  75. package/dist/runtime/components/Template/NAME.vue.d.ts +17 -0
  76. package/dist/runtime/components/TestControls/TestControls.vue +7 -10
  77. package/dist/runtime/components/TestControls/TestControls.vue.d.ts +5 -0
  78. package/dist/runtime/components/index.d.ts +12 -11
  79. package/dist/runtime/components/index.js +12 -11
  80. package/dist/runtime/components/shared/props.d.ts +81 -16
  81. package/dist/runtime/components/shared/storyHelpers/playInput.js +5 -5
  82. package/dist/runtime/components/shared/storyHelpers/playSuggestions.js +15 -11
  83. package/dist/runtime/composables/index.d.ts +5 -0
  84. package/dist/runtime/composables/index.js +5 -0
  85. package/dist/runtime/composables/useDivideAttrs.js +1 -0
  86. package/dist/runtime/composables/useDragWithThreshold.d.ts +71 -0
  87. package/dist/runtime/composables/useDragWithThreshold.js +40 -0
  88. package/dist/runtime/composables/usePreHydrationValue.d.ts +12 -0
  89. package/dist/runtime/composables/usePreHydrationValue.js +15 -0
  90. package/dist/runtime/composables/useSetupI18n.d.ts +2 -0
  91. package/dist/runtime/composables/useSetupI18n.js +5 -1
  92. package/dist/runtime/composables/useSuggestions.d.ts +7 -5
  93. package/dist/runtime/composables/useSuggestions.js +94 -57
  94. package/dist/runtime/directives/vResizableCols.js +92 -84
  95. package/dist/runtime/helpers/NotificationHandler.d.ts +5 -0
  96. package/dist/runtime/helpers/index.d.ts +3 -1
  97. package/dist/runtime/helpers/index.js +3 -1
  98. package/dist/runtime/types/index.d.ts +6 -0
  99. package/dist/runtime/utils/notifyIfError.d.ts +14 -0
  100. package/dist/runtime/utils/notifyIfError.js +29 -0
  101. package/dist/types.d.mts +2 -6
  102. package/package.json +27 -29
  103. package/src/module.ts +31 -12
  104. package/src/runtime/assets/base.css +10 -1
  105. package/src/runtime/assets/locales/en.json +2 -2
  106. package/src/runtime/assets/tailwind.css +1 -1
  107. package/src/runtime/assets/{style.css → utils.css} +86 -4
  108. package/src/runtime/build/WitchcraftUiResolver.ts +1 -1
  109. package/src/runtime/components/Focus.stories.ts +3 -2
  110. package/src/runtime/components/Icon/Icon.vue +10 -6
  111. package/src/runtime/components/LibButton/LibButton.vue +41 -47
  112. package/src/runtime/components/LibCheckbox/LibCheckbox.vue +7 -4
  113. package/src/runtime/components/LibColorInput/LibColorInput.vue +111 -37
  114. package/src/runtime/components/LibColorPicker/LibColorPicker.stories.ts +25 -4
  115. package/src/runtime/components/LibColorPicker/LibColorPicker.vue +242 -131
  116. package/src/runtime/components/LibColorPicker/utils/safeConvertToHsva.ts +24 -0
  117. package/src/runtime/components/LibColorPicker/utils/safeConvertToRgba.ts +23 -0
  118. package/src/runtime/components/LibColorPicker/utils/toLowPrecisionRgbaString.ts +13 -0
  119. package/src/runtime/components/LibColorPicker/utils/truncate.ts +6 -0
  120. package/src/runtime/components/LibDarkModeSwitcher/LibDarkModeSwitcher.stories.ts +1 -1
  121. package/src/runtime/components/LibDarkModeSwitcher/LibDarkModeSwitcher.vue +11 -9
  122. package/src/runtime/components/LibDatePicker/LibDatePicker.vue +4 -17
  123. package/src/runtime/components/LibDatePicker/LibRangeDatePicker.vue +192 -131
  124. package/src/runtime/components/LibDatePicker/LibSingleDatePicker.vue +183 -115
  125. package/src/runtime/components/LibDatePicker/LibTimeZonePicker.vue +3 -3
  126. package/src/runtime/components/LibDebug/LibDebug.vue +15 -5
  127. package/src/runtime/components/LibDevOnly/LibDevOnly.vue +1 -3
  128. package/src/runtime/components/LibFileInput/LibFileInput.vue +54 -29
  129. package/src/runtime/components/{LibInput/LibInput.stories.ts → LibInputDeprecated/LibInputDeprecated.stories.ts} +64 -19
  130. package/{dist/runtime/components/LibInput/LibInput.vue → src/runtime/components/LibInputDeprecated/LibInputDeprecated.vue} +40 -34
  131. package/src/runtime/components/LibLabel/LibLabel.vue +2 -2
  132. package/src/runtime/components/LibMultiValues/LibMultiValues.stories.ts +5 -4
  133. package/src/runtime/components/LibMultiValues/LibMultiValues.vue +11 -13
  134. package/src/runtime/components/LibNotifications/LibNotification.vue +19 -11
  135. package/src/runtime/components/LibNotifications/LibNotifications.stories.ts +2 -2
  136. package/src/runtime/components/LibNotifications/LibNotifications.vue +20 -12
  137. package/src/runtime/components/LibPagination/LibPagination.stories.ts +2 -2
  138. package/src/runtime/components/LibPagination/LibPagination.vue +19 -20
  139. package/src/runtime/components/LibPalette/LibPalette.vue +3 -3
  140. package/src/runtime/components/LibPopup/LibPopup.stories.ts +2 -2
  141. package/src/runtime/components/LibPopup/LibPopup.vue +30 -67
  142. package/src/runtime/components/LibProgressBar/LibProgressBar.vue +3 -2
  143. package/src/runtime/components/LibRecorder/LibRecorder.vue +2 -3
  144. package/src/runtime/components/LibRoot/LibRoot.vue +14 -1
  145. package/src/runtime/components/LibSimpleInput/LibSimpleInput.stories.ts +1 -1
  146. package/src/runtime/components/LibSimpleInput/LibSimpleInput.vue +5 -8
  147. package/src/runtime/components/LibSuggestions/LibSuggestions.vue +42 -26
  148. package/src/runtime/components/LibTable/LibTable.vue +8 -9
  149. package/src/runtime/components/Scrolling.stories.ts +58 -0
  150. package/src/runtime/components/Template/NAME.vue +1 -1
  151. package/src/runtime/components/TestControls/TestControls.vue +1 -1
  152. package/src/runtime/components/index.ts +12 -12
  153. package/src/runtime/components/shared/props.ts +82 -19
  154. package/src/runtime/components/shared/storyHelpers/playInput.ts +6 -5
  155. package/src/runtime/components/shared/storyHelpers/playSuggestions.ts +25 -11
  156. package/src/runtime/composables/index.ts +5 -0
  157. package/src/runtime/composables/useDarkMode.ts +2 -2
  158. package/src/runtime/composables/useDivideAttrs.ts +1 -0
  159. package/src/runtime/composables/useDragWithThreshold.ts +108 -0
  160. package/src/runtime/composables/usePreHydrationValue.ts +30 -0
  161. package/src/runtime/composables/useSetupI18n.ts +8 -2
  162. package/src/runtime/composables/useSuggestions.ts +92 -45
  163. package/src/runtime/directives/vResizableCols.ts +82 -74
  164. package/src/runtime/helpers/NotificationHandler.ts +5 -0
  165. package/src/runtime/helpers/index.ts +3 -1
  166. package/src/runtime/types/index.ts +5 -0
  167. package/src/runtime/utils/notifyIfError.ts +45 -0
  168. package/dist/module.cjs +0 -5
  169. package/dist/module.d.ts +0 -34
  170. package/dist/runtime/assets/style.css +0 -1
  171. package/dist/runtime/components/Focus.stories.d.ts +0 -11
  172. package/dist/runtime/components/Focus.stories.js +0 -53
  173. package/dist/runtime/components/LibButton/LibButton.stories.d.ts +0 -12
  174. package/dist/runtime/components/LibButton/LibButton.stories.js +0 -94
  175. package/dist/runtime/components/LibCheckbox/LibCheckbox.stories.d.ts +0 -14
  176. package/dist/runtime/components/LibCheckbox/LibCheckbox.stories.js +0 -29
  177. package/dist/runtime/components/LibColorInput/LibColorInput.stories.d.ts +0 -7
  178. package/dist/runtime/components/LibColorInput/LibColorInput.stories.js +0 -58
  179. package/dist/runtime/components/LibColorPicker/LibColorPicker.stories.d.ts +0 -7
  180. package/dist/runtime/components/LibColorPicker/LibColorPicker.stories.js +0 -51
  181. package/dist/runtime/components/LibDarkModeSwitcher/LibDarkModeSwitcher.stories.d.ts +0 -7
  182. package/dist/runtime/components/LibDarkModeSwitcher/LibDarkModeSwitcher.stories.js +0 -36
  183. package/dist/runtime/components/LibDatePicker/LibDatePicker.stories.d.ts +0 -11
  184. package/dist/runtime/components/LibDatePicker/LibDatePicker.stories.js +0 -98
  185. package/dist/runtime/components/LibDebug/LibDebug.stories.d.ts +0 -9
  186. package/dist/runtime/components/LibDebug/LibDebug.stories.js +0 -46
  187. package/dist/runtime/components/LibFileInput/LibFileInput.stories.d.ts +0 -10
  188. package/dist/runtime/components/LibFileInput/LibFileInput.stories.js +0 -63
  189. package/dist/runtime/components/LibInput/LibInput.stories.d.ts +0 -33
  190. package/dist/runtime/components/LibInput/LibInput.stories.js +0 -339
  191. package/dist/runtime/components/LibLabel/LibLabel.stories.d.ts +0 -6
  192. package/dist/runtime/components/LibLabel/LibLabel.stories.js +0 -25
  193. package/dist/runtime/components/LibMultiValues/LibMultiValues.stories.d.ts +0 -23
  194. package/dist/runtime/components/LibMultiValues/LibMultiValues.stories.js +0 -60
  195. package/dist/runtime/components/LibNotifications/LibNotification.stories.d.ts +0 -15
  196. package/dist/runtime/components/LibNotifications/LibNotification.stories.js +0 -126
  197. package/dist/runtime/components/LibNotifications/LibNotifications.stories.d.ts +0 -6
  198. package/dist/runtime/components/LibNotifications/LibNotifications.stories.js +0 -109
  199. package/dist/runtime/components/LibPagination/LibPagination.stories.d.ts +0 -6
  200. package/dist/runtime/components/LibPagination/LibPagination.stories.js +0 -40
  201. package/dist/runtime/components/LibPalette/LibPalette.stories.d.ts +0 -6
  202. package/dist/runtime/components/LibPalette/LibPalette.stories.js +0 -20
  203. package/dist/runtime/components/LibPopup/LibPopup.stories.d.ts +0 -14
  204. package/dist/runtime/components/LibPopup/LibPopup.stories.js +0 -147
  205. package/dist/runtime/components/LibProgressBar/LibProgressBar.stories.d.ts +0 -10
  206. package/dist/runtime/components/LibProgressBar/LibProgressBar.stories.js +0 -81
  207. package/dist/runtime/components/LibRecorder/LibRecorder.stories.d.ts +0 -19
  208. package/dist/runtime/components/LibRecorder/LibRecorder.stories.js +0 -63
  209. package/dist/runtime/components/LibSimpleInput/LibSimpleInput.stories.d.ts +0 -26
  210. package/dist/runtime/components/LibSimpleInput/LibSimpleInput.stories.js +0 -78
  211. package/dist/runtime/components/LibSuggestions/LibSuggestions.stories.d.ts +0 -27
  212. package/dist/runtime/components/LibSuggestions/LibSuggestions.stories.js +0 -112
  213. package/dist/runtime/components/LibTable/LibTable.stories.d.ts +0 -16
  214. package/dist/runtime/components/LibTable/LibTable.stories.js +0 -156
  215. package/dist/runtime/components/reset.stories.d.ts +0 -5
  216. package/dist/runtime/components/reset.stories.js +0 -19
  217. package/dist/runtime/composables/useScrollNearContainerEdges.stories.d.ts +0 -7
  218. package/dist/runtime/composables/useScrollNearContainerEdges.stories.js +0 -85
  219. package/dist/runtime/helpers/addValue.d.ts +0 -1
  220. package/dist/runtime/helpers/addValue.js +0 -8
  221. package/dist/types.d.ts +0 -7
  222. package/src/runtime/components/LibInput/LibInput.vue +0 -372
  223. package/src/runtime/helpers/addValue.ts +0 -10
  224. /package/src/runtime/components/{reset.stories.ts → Reset.stories.ts} +0 -0
@@ -0,0 +1,108 @@
1
+ import { type Ref, ref } from "vue"
2
+
3
+ import type { Point } from "../types/index.js"
4
+
5
+
6
+ /**
7
+ * A composable for dragging items only after a certain threshold of movement has been reached.
8
+ *
9
+ * What to do with the actual pointer coordinates is up to you.
10
+ *
11
+ * The is compatible with the `useScrollNearContainerEdges` composable as well.
12
+ *
13
+ * ```ts
14
+ * const {
15
+ * pointerCoords,
16
+ * passedDragThreshold,
17
+ * startDragThresholdCheck,
18
+ * endDragThresholdCheck,
19
+ * checkDragThreshold,
20
+ * } = useDragWithThreshold({ threshold: ref(5) })
21
+ *
22
+ * function grabPointerDown(e: PointerEvent): void {
23
+ * startDragThresholdCheck(e)
24
+ * e.preventDefault()
25
+ *
26
+ * document.addEventListener("pointermove", grabPointerMove)
27
+ * document.addEventListener("pointerup", grabPointerUp)
28
+ * document.addEventListener("keyup", escapeDrag)
29
+ * }
30
+ * function grabPointerMove(e: PointerEvent): void {
31
+ * e.preventDefault()
32
+ * checkDragThreshold(e)
33
+ * if (passedDragThreshold.value) {
34
+ *
35
+ * }
36
+ * }
37
+ * function grabPointerUp(e: PointerEvent): void {
38
+ * if (passedDragThreshold.value) {
39
+ * // drag
40
+ * } else {
41
+ * // handleAsClick(e)
42
+ * }
43
+ * stopDrag()
44
+ * }
45
+ *
46
+ * function stopDrag(): void {
47
+ * endDragThresholdCheck()
48
+ * document.removeEventListener("keyup", escapeDrag)
49
+ * document.removeEventListener("pointermove", grabPointerMove)
50
+ * document.removeEventListener("pointerup", grabPointerUp)
51
+ * }
52
+ *
53
+ * function escapeDrag(e: KeyboardEvent): void {
54
+ * if (e.code === "Escape") stopDrag()
55
+ * }
56
+ * ```
57
+ */
58
+ export const useDragWithThreshold = ({
59
+ initialOffset = ref({ x: 0, y: 0 }),
60
+ pointerCoords = ref({ x: 0, y: 0 }),
61
+ threshold = ref(10),
62
+ }: {
63
+ initialOffset?: Ref<Point | undefined>
64
+ pointerCoords?: Ref<Point | undefined>
65
+ threshold?: Ref<number>
66
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
67
+ } = {}) => {
68
+ const passedDragThreshold = ref(false)
69
+ function getDistance(p1: Point, p2: Point): number {
70
+ const xDiff = p2.x - p1.x
71
+ const yDiff = p2.y - p1.y
72
+
73
+ return Math.sqrt((xDiff * xDiff) + (yDiff * yDiff))
74
+ }
75
+
76
+ function getEventCoords(e: { clientX: number, clientY: number }): Point {
77
+ return { x: e.clientX, y: e.clientY }
78
+ }
79
+
80
+ function checkDragThreshold(e: PointerEvent): void {
81
+ pointerCoords.value = getEventCoords(e)
82
+ // don't allow it to be reset to false
83
+ passedDragThreshold.value ||=
84
+ initialOffset.value !== undefined &&
85
+ getDistance(initialOffset.value, pointerCoords.value) >= threshold.value
86
+ }
87
+ function startDragThresholdCheck(e: PointerEvent): void {
88
+ passedDragThreshold.value = false
89
+ initialOffset.value = getEventCoords(e)
90
+ pointerCoords.value = getEventCoords(e)
91
+ }
92
+ function endDragThresholdCheck(): void {
93
+ passedDragThreshold.value = false
94
+ initialOffset.value = undefined
95
+ pointerCoords.value = undefined
96
+ }
97
+ return {
98
+ initialOffset,
99
+ pointerCoords,
100
+ threshold,
101
+ passedDragThreshold,
102
+ getEventCoords,
103
+ checkDragThreshold,
104
+ startDragThresholdCheck,
105
+ endDragThresholdCheck,
106
+ }
107
+ }
108
+
@@ -0,0 +1,30 @@
1
+ import { onBeforeMount, onMounted, type Ref } from "vue"
2
+ /**
3
+ *
4
+ * This takes the value of the element at the given id (onBeforeMount, i.e. pre-hydration if available), and sets the given ref to that value onMounted.
5
+ *
6
+ * This should be compatible with both nuxt and vue.
7
+ *
8
+ * Nore that while it's generic, you will need to provide a transform function to convert the string to the desired type.
9
+ *
10
+ * @experimental
11
+ */
12
+ export function usePreHydrationValue<T>(
13
+ id: string,
14
+ refVal: Ref<T>,
15
+ transform: (val: string) => T = val => val as any
16
+ ): void {
17
+ // directly setting it in the onBeforeMount hook doesn't work
18
+ let temp = ""
19
+ onBeforeMount(() => {
20
+ const el = document.getElementById(id) as HTMLInputElement
21
+ if (el?.value) {
22
+ temp = el.value
23
+ }
24
+ })
25
+ onMounted(() => {
26
+ if (temp) {
27
+ refVal.value = transform(temp)
28
+ }
29
+ })
30
+ }
@@ -36,6 +36,8 @@ const dummyMessageSet = new Proxy({}, {
36
36
  * Should be called only once. You can choose to await it or not (see the `useDummyMessageSetWhileLoading` option).
37
37
  *
38
38
  * A default function is available, see {@link defaultTranslationFunction}.
39
+ *
40
+ * To avoid hydration errors, on the server, the message loading is awaited.
39
41
  */
40
42
  export async function useSetupI18n({
41
43
  locale,
@@ -60,12 +62,16 @@ export async function useSetupI18n({
60
62
  if (isLoaded) {
61
63
  messages.value = loaded[l]
62
64
  } else {
63
- const newMessages = ((await messagesGlob[`../assets/locales/${l}.json`]()) as any).default as any
65
+ const newMessages = ((await messagesGlob[`../assets/locales/${l}.json`]!()) as any).default as any
64
66
  loaded[l] = newMessages
65
67
  messages.value = newMessages
66
68
  }
67
69
  }
68
- void loadMessageSet(locale.value)
70
+ if (import.meta.server) {
71
+ await loadMessageSet(locale.value)
72
+ } else {
73
+ void loadMessageSet(locale.value)
74
+ }
69
75
  watch(locale, async () => {
70
76
  void loadMessageSet(locale.value)
71
77
  })
@@ -1,6 +1,9 @@
1
1
  /* eslint-disable no-console */
2
+ import { type AnyFunction } from "@alanscodelog/utils"
2
3
  import { isBlank } from "@alanscodelog/utils/isBlank.js"
3
4
  import { isObject } from "@alanscodelog/utils/isObject.js"
5
+ import { pushIfNotIn } from "@alanscodelog/utils/pushIfNotIn.js"
6
+ import { removeIfIn } from "@alanscodelog/utils/removeIfIn.js"
4
7
  import { computed, type Ref, ref, toRaw, watch } from "vue"
5
8
 
6
9
  import { type SuggestionsEmits,type SuggestionsOptions } from "../components/shared/props.js"
@@ -12,9 +15,10 @@ import { type SuggestionsEmits,type SuggestionsOptions } from "../components/sha
12
15
  * Note that while object suggestions are supported, the `suggestionLabel` prop is required and $inputModel and $modelValue will still be string values (as returned by the suggestionLabel function).
13
16
  */
14
17
  // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
15
- export function useSuggestions<TSuggestion>(
18
+ export function useSuggestions<TSuggestion, TMultivalue extends boolean = false>(
16
19
  $inputValue: Ref<string>,
17
- $modelValue: Ref<string>,
20
+ $modelValue: Ref<TMultivalue extends true ? string[] : string>,
21
+ $open: Ref<boolean>,
18
22
  emit: SuggestionsEmits,
19
23
  opts: SuggestionsOptions<TSuggestion>,
20
24
  debug: boolean = false
@@ -23,29 +27,40 @@ export function useSuggestions<TSuggestion>(
23
27
  throw new Error("`suggestionLabel` or `suggestionsFilter` must be passed if suggestions are objects.")
24
28
  }
25
29
 
26
- const isOpen = ref(false)
27
30
  const activeSuggestion = ref(-1)
28
- watch(isOpen, val => { emit("update:isOpen", val) })
29
31
  watch(activeSuggestion, val => { emit("update:activeSuggestion", val) })
30
- const suggestionKey = (item: any): string => {
32
+ if (opts.suggestions) {
33
+ for (const suggestion of opts.suggestions) {
34
+ suggestionLabelGuard(suggestion, opts.suggestionLabel)
35
+ }
36
+ }
37
+
38
+ const getSuggestionLabel = (item: any): string => {
39
+ suggestionLabelGuard(item, opts.suggestionLabel)
31
40
  if (isObject<any>(item)) {
32
- if (opts.suggestionLabel) {
33
- return opts.suggestionLabel(item)
34
- }
35
- throw new Error("`suggestionLabel` must be passed if suggestions are objects.")
41
+ return opts.suggestionLabel!(item)
36
42
  }
37
43
  return item
38
44
  }
39
- const getSuggestionLabel = (item: any): string => opts.suggestionLabel?.(item) ?? suggestionKey(item) ?? ""
40
45
 
41
46
  const defaultSuggestionsFilter = (input: string, items: TSuggestion[]): TSuggestion[] => input === ""
42
47
  ? [...items]
43
- : items.filter(item => getSuggestionLabel(item).toLowerCase().includes(input.toLowerCase()))
48
+ : items.filter(item => {
49
+ if (Array.isArray($modelValue.value)) {
50
+ // always include selected values for unselecting
51
+ if ($modelValue.value.includes(getSuggestionLabel(item))) return true
52
+ }
53
+ return getSuggestionLabel(item).toLowerCase().includes(input.toLowerCase())
54
+ })
44
55
  const suggestionsFilter = computed(() => opts.suggestionsFilter ?? defaultSuggestionsFilter)
45
56
 
46
57
  const suggestionsList = computed(() => {
47
58
  if (opts.suggestions) {
48
- const res = suggestionsFilter.value($inputValue.value, opts.suggestions)
59
+ const suggestions = [...opts.suggestions]
60
+ if (Array.isArray($modelValue.value) && !opts.showSelectedValues) {
61
+ pushIfNotIn(suggestions, $modelValue.value)
62
+ }
63
+ const res = suggestionsFilter.value($inputValue.value, suggestions)
49
64
  return res
50
65
  }
51
66
  return undefined
@@ -79,9 +94,6 @@ export function useSuggestions<TSuggestion>(
79
94
  : opts.suggestions!)
80
95
 
81
96
  if (opts.restrictToSuggestions && !isValidSuggestion.value) return res
82
- if (opts.preventDuplicateValues && opts.values) {
83
- return res.filter(suggestion => !opts.values!.includes(suggestionKey(suggestion)))
84
- }
85
97
  return res
86
98
  }
87
99
  return undefined
@@ -89,13 +101,29 @@ export function useSuggestions<TSuggestion>(
89
101
 
90
102
 
91
103
  // methods
104
+ // returns true if the value was removed
105
+ function setValue(val: string): boolean {
106
+ if (Array.isArray($modelValue.value)) {
107
+ // works like a toggle
108
+ if ($modelValue.value.includes(val)) {
109
+ removeIfIn($modelValue.value, val)
110
+ return true
111
+ } else {
112
+ pushIfNotIn($modelValue.value, [val])
113
+ }
114
+ } else {
115
+ ($modelValue.value as string) = val
116
+ }
117
+ return false
118
+ }
92
119
 
93
- const closeSuggestions = (): void => {
120
+ function closeSuggestions(): void {
121
+ if (!opts.canClose) return
94
122
  if (debug) console.log("closeSuggestions")
95
- isOpen.value = false
123
+ $open.value = false
96
124
  activeSuggestion.value = -1
97
125
  }
98
- const openSuggestions = (): void => {
126
+ function openSuggestions(): void {
99
127
  if (debug) console.log("openSuggestions", { openable: openable.value })
100
128
  if (!openable.value) return
101
129
  if (activeSuggestion.value === -1) {
@@ -105,23 +133,25 @@ export function useSuggestions<TSuggestion>(
105
133
  activeSuggestion.value = 0
106
134
  }
107
135
  }
108
- isOpen.value = true
136
+ $open.value = true
109
137
  }
110
138
 
111
- function enterSuggestion(num: number): void {
139
+ function enterSuggestion(num: number, doClose: boolean = true): void {
112
140
  if (num < -1 || num > (filteredSuggestions.value?.length ?? 0)) return
113
141
  if (debug) console.log("enterSuggestion", num)
114
142
  if (filteredSuggestions.value === undefined) return
115
143
 
116
144
  const suggestion = filteredSuggestions.value[num]
117
- const val = suggestionKey(suggestion)
118
- $modelValue.value = val
119
- $inputValue.value = getSuggestionLabel(suggestion)
120
- closeSuggestions()
121
- emit("submit", val, toRaw(suggestion))
145
+ const val = getSuggestionLabel(suggestion)
146
+ const wasRemoved = setValue(val)
147
+ $inputValue.value = Array.isArray($modelValue.value) ? "" : getSuggestionLabel(suggestion)
148
+ if (doClose) {
149
+ closeSuggestions()
150
+ }
151
+ emit("submit", val, toRaw(suggestion), wasRemoved)
122
152
  }
123
153
 
124
- const enterSelected = (): void => {
154
+ function enterSelected(doClose: boolean = true): void {
125
155
  if (activeSuggestion.value === -1) {
126
156
  if (!opts.restrictToSuggestions) {
127
157
  if (debug) console.log("enterSelected, unrestricted, emitting submit")
@@ -132,9 +162,10 @@ export function useSuggestions<TSuggestion>(
132
162
  return
133
163
  }
134
164
  if (debug) console.log("enterSelected")
135
- enterSuggestion(activeSuggestion.value)
165
+ enterSuggestion(activeSuggestion.value, doClose)
136
166
  }
137
- const selectSuggestion = (num: number): void => {
167
+
168
+ function selectSuggestion(num: number): void {
138
169
  if (debug) console.log("selectSuggestion", num)
139
170
  if (num >= -1) {
140
171
  activeSuggestion.value = num
@@ -144,11 +175,11 @@ export function useSuggestions<TSuggestion>(
144
175
  }
145
176
  }
146
177
 
147
- const toggleSuggestions = (): void => {
148
- isOpen.value ? closeSuggestions() : openSuggestions()
178
+ function toggleSuggestions(): void {
179
+ $open.value ? closeSuggestions() : openSuggestions()
149
180
  }
150
181
 
151
- const prevSuggestion = (): void => {
182
+ function prevSuggestion(): void {
152
183
  if (!filteredSuggestions.value) return
153
184
  if (activeSuggestion.value > 0) {
154
185
  activeSuggestion.value--
@@ -157,7 +188,7 @@ export function useSuggestions<TSuggestion>(
157
188
  }
158
189
  }
159
190
 
160
- const nextSuggestion = (): void => {
191
+ function nextSuggestion(): void {
161
192
  if (!filteredSuggestions.value) return
162
193
  if (activeSuggestion.value >= filteredSuggestions.value.length - 1) {
163
194
  activeSuggestion.value = 0
@@ -165,14 +196,18 @@ export function useSuggestions<TSuggestion>(
165
196
  activeSuggestion.value++
166
197
  }
167
198
  }
168
- const firstSuggestion = (): void => {
199
+ function firstSuggestion(): void {
169
200
  selectSuggestion(0)
170
201
  }
171
- const lastSuggestion = (): void => {
202
+ function lastSuggestion(): void {
172
203
  selectSuggestion(Infinity)
173
204
  }
174
205
 
175
- const cancel = (): void => {
206
+ function cancel(): void {
207
+ if (Array.isArray($modelValue.value)) {
208
+ $inputValue.value = ""
209
+ return
210
+ }
176
211
  if (debug) console.log("cancel")
177
212
  $inputValue.value = getSuggestionLabel($modelValue.value)
178
213
  closeSuggestions()
@@ -203,11 +238,15 @@ export function useSuggestions<TSuggestion>(
203
238
  // sync vmodels and vmodel effects
204
239
 
205
240
  watch($modelValue, () => {
206
- $inputValue.value = getSuggestionLabel($modelValue.value)
241
+ if (Array.isArray($modelValue.value)) {
242
+ $inputValue.value = ""
243
+ } else {
244
+ $inputValue.value = getSuggestionLabel($modelValue.value)
245
+ }
207
246
  if (debug) console.log("modelValue changed")
208
247
  })
209
248
 
210
- const defaultSuggestionSelector = (suggestions: TSuggestion[], input: string): number => {
249
+ function defaultSuggestionSelector(suggestions: TSuggestion[], input: string): number {
211
250
  if (input.length === 0) return 0
212
251
  let longestMatch
213
252
  let ii = -1
@@ -227,16 +266,16 @@ export function useSuggestions<TSuggestion>(
227
266
 
228
267
  watch($inputValue, () => {
229
268
  if (debug) console.log("input changed:", $inputValue.value, "modelValue:", $modelValue.value)
230
- if (getSuggestionLabel($modelValue.value) === $inputValue.value) return
269
+ if (!Array.isArray($modelValue.value) && getSuggestionLabel($modelValue.value) === $inputValue.value) return
231
270
 
232
271
  if (suggestionAvailable.value) {
233
272
  if (debug) console.log("input changed, suggestion available, opening suggestions")
234
273
  openSuggestions()
235
274
  }
236
275
 
237
- if (!opts.restrictToSuggestions) {
276
+ if (!opts.restrictToSuggestions && !Array.isArray($modelValue.value)) {
238
277
  if (debug) console.log("input changed, unrestricted, setting modelValue")
239
- $modelValue.value = $inputValue.value
278
+ setValue($inputValue.value)
240
279
  }
241
280
  if (exactlyMatchingSuggestion.value && suggestionsList.value) {
242
281
  if (debug) console.log("input changed, exactly matching, setting activeSuggestion")
@@ -264,11 +303,11 @@ export function useSuggestions<TSuggestion>(
264
303
  hasValidSuggestion: isValidSuggestion,
265
304
  openable,
266
305
  getLabel: getSuggestionLabel,
267
- isOpen,
306
+ $open,
268
307
  open: openSuggestions,
269
308
  close: closeSuggestions,
270
309
  enterSelected,
271
- enterSuggestion,
310
+ enterIndex: enterSuggestion,
272
311
  toggle: toggleSuggestions,
273
312
  cancel,
274
313
  select: selectSuggestion,
@@ -276,13 +315,14 @@ export function useSuggestions<TSuggestion>(
276
315
  next: nextSuggestion,
277
316
  first: firstSuggestion,
278
317
  last: lastSuggestion,
318
+
279
319
  }
280
320
  }
281
321
 
282
322
  // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
283
323
  export function useSuggestionsInputAria(
284
324
  id: Ref<string>,
285
- isOpen: Ref<boolean>,
325
+ $open: Ref<boolean>,
286
326
  activeSuggestion: Ref<number>,
287
327
  suggestions: Ref<any | undefined>
288
328
  ) {
@@ -290,8 +330,15 @@ export function useSuggestionsInputAria(
290
330
  "aria-autocomplete": suggestions !== undefined ? "both" as const : undefined,
291
331
  "aria-controls": suggestions !== undefined ? `suggestions-${id.value}` : undefined,
292
332
  role: suggestions ? "combobox" : undefined,
293
- "aria-expanded": suggestions !== undefined ? isOpen.value : undefined,
294
- "aria-activedescendant": isOpen.value ? `suggestion-${id.value}-${activeSuggestion.value}` : undefined,
333
+ "aria-expanded": suggestions !== undefined ? $open.value : undefined,
334
+ "aria-activedescendant": $open.value ? `suggestion-${id.value}-${activeSuggestion.value}` : undefined,
295
335
  }))
296
336
  return ariaInputProps
297
337
  }
338
+ export function suggestionLabelGuard<TFunction extends AnyFunction>(item: any, suggestionLabeler: TFunction | undefined): asserts suggestionLabeler is TFunction {
339
+ if (isObject<any>(item)) {
340
+ if (!suggestionLabeler) {
341
+ throw new Error("`suggestionLabel` must be passed if suggestions are objects.")
342
+ }
343
+ }
344
+ }