@witchcraft/ui 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/README.md +18 -28
  2. package/dist/module.d.mts +3 -1
  3. package/dist/module.d.ts +3 -1
  4. package/dist/module.json +2 -2
  5. package/dist/module.mjs +20 -11
  6. package/dist/runtime/assets/base.css +1 -1
  7. package/dist/runtime/assets/locales/en.json +2 -2
  8. package/dist/runtime/assets/tailwind.css +1 -1
  9. package/dist/runtime/assets/utils.css +1 -0
  10. package/dist/runtime/build/WitchcraftUiResolver.js +1 -1
  11. package/dist/runtime/components/Icon/Icon.vue +10 -5
  12. package/dist/runtime/components/LibButton/LibButton.vue +41 -46
  13. package/dist/runtime/components/LibCheckbox/LibCheckbox.vue +7 -3
  14. package/dist/runtime/components/LibColorInput/LibColorInput.vue +111 -36
  15. package/dist/runtime/components/LibColorPicker/LibColorPicker.stories.d.ts +2 -0
  16. package/dist/runtime/components/LibColorPicker/LibColorPicker.stories.js +26 -9
  17. package/dist/runtime/components/LibColorPicker/LibColorPicker.vue +242 -131
  18. package/dist/runtime/components/LibColorPicker/utils/safeConvertToHsva.d.ts +2 -0
  19. package/dist/runtime/components/LibColorPicker/utils/safeConvertToHsva.js +18 -0
  20. package/dist/runtime/components/LibColorPicker/utils/safeConvertToRgba.d.ts +2 -0
  21. package/dist/runtime/components/LibColorPicker/utils/safeConvertToRgba.js +17 -0
  22. package/dist/runtime/components/LibColorPicker/utils/toLowPrecisionRgbaString.d.ts +2 -0
  23. package/dist/runtime/components/LibColorPicker/utils/toLowPrecisionRgbaString.js +8 -0
  24. package/dist/runtime/components/LibColorPicker/utils/truncate.d.ts +1 -0
  25. package/dist/runtime/components/LibColorPicker/utils/truncate.js +5 -0
  26. package/dist/runtime/components/LibDarkModeSwitcher/LibDarkModeSwitcher.stories.js +1 -1
  27. package/dist/runtime/components/LibDarkModeSwitcher/LibDarkModeSwitcher.vue +11 -8
  28. package/dist/runtime/components/LibDatePicker/LibDatePicker.vue +4 -17
  29. package/dist/runtime/components/LibDatePicker/LibRangeDatePicker.vue +192 -131
  30. package/dist/runtime/components/LibDatePicker/LibSingleDatePicker.vue +183 -115
  31. package/dist/runtime/components/LibDatePicker/LibTimeZonePicker.vue +3 -3
  32. package/dist/runtime/components/LibDebug/LibDebug.vue +15 -5
  33. package/dist/runtime/components/LibDevOnly/LibDevOnly.vue +1 -3
  34. package/dist/runtime/components/LibFileInput/LibFileInput.vue +54 -28
  35. package/dist/runtime/components/{LibInput/LibInput.stories.d.ts → LibInputDeprecated/LibInputDeprecated.stories.d.ts} +6 -6
  36. package/dist/runtime/components/{LibInput/LibInput.stories.js → LibInputDeprecated/LibInputDeprecated.stories.js} +64 -19
  37. package/{src/runtime/components/LibInput/LibInput.vue → dist/runtime/components/LibInputDeprecated/LibInputDeprecated.vue} +40 -33
  38. package/dist/runtime/components/LibLabel/LibLabel.vue +2 -2
  39. package/dist/runtime/components/LibMultiValues/LibMultiValues.stories.d.ts +1 -1
  40. package/dist/runtime/components/LibMultiValues/LibMultiValues.stories.js +5 -4
  41. package/dist/runtime/components/LibMultiValues/LibMultiValues.vue +11 -12
  42. package/dist/runtime/components/LibNotifications/LibNotification.vue +19 -10
  43. package/dist/runtime/components/LibNotifications/LibNotifications.stories.js +2 -2
  44. package/dist/runtime/components/LibNotifications/LibNotifications.vue +20 -11
  45. package/dist/runtime/components/LibPagination/LibPagination.stories.js +2 -2
  46. package/dist/runtime/components/LibPagination/LibPagination.vue +19 -19
  47. package/dist/runtime/components/LibPalette/LibPalette.vue +3 -3
  48. package/dist/runtime/components/LibPopup/LibPopup.stories.js +2 -2
  49. package/dist/runtime/components/LibPopup/LibPopup.vue +30 -66
  50. package/dist/runtime/components/LibProgressBar/LibProgressBar.vue +3 -1
  51. package/dist/runtime/components/LibRecorder/LibRecorder.vue +2 -2
  52. package/dist/runtime/components/LibRoot/LibRoot.vue +14 -1
  53. package/dist/runtime/components/LibSimpleInput/LibSimpleInput.stories.js +1 -1
  54. package/dist/runtime/components/LibSimpleInput/LibSimpleInput.vue +5 -7
  55. package/dist/runtime/components/LibSuggestions/LibSuggestions.vue +42 -25
  56. package/dist/runtime/components/LibTable/LibTable.vue +8 -8
  57. package/dist/runtime/components/Scrolling.stories.d.ts +6 -0
  58. package/dist/runtime/components/Scrolling.stories.js +44 -0
  59. package/dist/runtime/components/Template/NAME.vue +1 -1
  60. package/dist/runtime/components/TestControls/TestControls.vue +1 -1
  61. package/dist/runtime/components/index.d.ts +12 -11
  62. package/dist/runtime/components/index.js +12 -11
  63. package/dist/runtime/components/shared/props.d.ts +81 -16
  64. package/dist/runtime/components/shared/storyHelpers/playInput.js +5 -5
  65. package/dist/runtime/components/shared/storyHelpers/playSuggestions.js +15 -11
  66. package/dist/runtime/composables/index.d.ts +5 -0
  67. package/dist/runtime/composables/index.js +5 -0
  68. package/dist/runtime/composables/useDivideAttrs.js +1 -0
  69. package/dist/runtime/composables/useDragWithThreshold.d.ts +71 -0
  70. package/dist/runtime/composables/useDragWithThreshold.js +40 -0
  71. package/dist/runtime/composables/usePreHydrationValue.d.ts +12 -0
  72. package/dist/runtime/composables/usePreHydrationValue.js +15 -0
  73. package/dist/runtime/composables/useSetupI18n.d.ts +2 -0
  74. package/dist/runtime/composables/useSetupI18n.js +5 -1
  75. package/dist/runtime/composables/useSuggestions.d.ts +7 -5
  76. package/dist/runtime/composables/useSuggestions.js +94 -57
  77. package/dist/runtime/directives/vResizableCols.js +3 -1
  78. package/dist/runtime/helpers/NotificationHandler.d.ts +5 -0
  79. package/dist/runtime/helpers/index.d.ts +3 -1
  80. package/dist/runtime/helpers/index.js +3 -1
  81. package/dist/runtime/types/index.d.ts +6 -0
  82. package/dist/runtime/utils/notifyIfError.d.ts +14 -0
  83. package/dist/runtime/utils/notifyIfError.js +29 -0
  84. package/package.json +18 -20
  85. package/src/module.ts +31 -12
  86. package/src/runtime/assets/base.css +10 -1
  87. package/src/runtime/assets/locales/en.json +2 -2
  88. package/src/runtime/assets/tailwind.css +1 -1
  89. package/src/runtime/assets/{style.css → utils.css} +86 -4
  90. package/src/runtime/build/WitchcraftUiResolver.ts +1 -1
  91. package/src/runtime/components/Icon/Icon.vue +10 -5
  92. package/src/runtime/components/LibButton/LibButton.vue +41 -46
  93. package/src/runtime/components/LibCheckbox/LibCheckbox.vue +7 -3
  94. package/src/runtime/components/LibColorInput/LibColorInput.vue +111 -36
  95. package/src/runtime/components/LibColorPicker/LibColorPicker.stories.ts +25 -4
  96. package/src/runtime/components/LibColorPicker/LibColorPicker.vue +242 -131
  97. package/src/runtime/components/LibColorPicker/utils/safeConvertToHsva.ts +25 -0
  98. package/src/runtime/components/LibColorPicker/utils/safeConvertToRgba.ts +23 -0
  99. package/src/runtime/components/LibColorPicker/utils/toLowPrecisionRgbaString.ts +13 -0
  100. package/src/runtime/components/LibColorPicker/utils/truncate.ts +6 -0
  101. package/src/runtime/components/LibDarkModeSwitcher/LibDarkModeSwitcher.stories.ts +1 -1
  102. package/src/runtime/components/LibDarkModeSwitcher/LibDarkModeSwitcher.vue +11 -8
  103. package/src/runtime/components/LibDatePicker/LibDatePicker.vue +4 -17
  104. package/src/runtime/components/LibDatePicker/LibRangeDatePicker.vue +192 -131
  105. package/src/runtime/components/LibDatePicker/LibSingleDatePicker.vue +183 -115
  106. package/src/runtime/components/LibDatePicker/LibTimeZonePicker.vue +3 -3
  107. package/src/runtime/components/LibDebug/LibDebug.vue +15 -5
  108. package/src/runtime/components/LibDevOnly/LibDevOnly.vue +1 -3
  109. package/src/runtime/components/LibFileInput/LibFileInput.vue +54 -28
  110. package/src/runtime/components/{LibInput/LibInput.stories.ts → LibInputDeprecated/LibInputDeprecated.stories.ts} +64 -19
  111. package/{dist/runtime/components/LibInput/LibInput.vue → src/runtime/components/LibInputDeprecated/LibInputDeprecated.vue} +40 -33
  112. package/src/runtime/components/LibLabel/LibLabel.vue +2 -2
  113. package/src/runtime/components/LibMultiValues/LibMultiValues.stories.ts +5 -4
  114. package/src/runtime/components/LibMultiValues/LibMultiValues.vue +11 -12
  115. package/src/runtime/components/LibNotifications/LibNotification.vue +19 -10
  116. package/src/runtime/components/LibNotifications/LibNotifications.stories.ts +2 -2
  117. package/src/runtime/components/LibNotifications/LibNotifications.vue +20 -11
  118. package/src/runtime/components/LibPagination/LibPagination.stories.ts +2 -2
  119. package/src/runtime/components/LibPagination/LibPagination.vue +19 -19
  120. package/src/runtime/components/LibPalette/LibPalette.vue +3 -3
  121. package/src/runtime/components/LibPopup/LibPopup.stories.ts +2 -2
  122. package/src/runtime/components/LibPopup/LibPopup.vue +30 -66
  123. package/src/runtime/components/LibProgressBar/LibProgressBar.vue +3 -1
  124. package/src/runtime/components/LibRecorder/LibRecorder.vue +2 -2
  125. package/src/runtime/components/LibRoot/LibRoot.vue +14 -1
  126. package/src/runtime/components/LibSimpleInput/LibSimpleInput.stories.ts +1 -1
  127. package/src/runtime/components/LibSimpleInput/LibSimpleInput.vue +5 -7
  128. package/src/runtime/components/LibSuggestions/LibSuggestions.vue +42 -25
  129. package/src/runtime/components/LibTable/LibTable.vue +8 -8
  130. package/src/runtime/components/Scrolling.stories.ts +58 -0
  131. package/src/runtime/components/Template/NAME.vue +1 -1
  132. package/src/runtime/components/TestControls/TestControls.vue +1 -1
  133. package/src/runtime/components/index.ts +12 -12
  134. package/src/runtime/components/shared/props.ts +82 -19
  135. package/src/runtime/components/shared/storyHelpers/playInput.ts +6 -5
  136. package/src/runtime/components/shared/storyHelpers/playSuggestions.ts +25 -11
  137. package/src/runtime/composables/index.ts +5 -0
  138. package/src/runtime/composables/useDarkMode.ts +2 -2
  139. package/src/runtime/composables/useDivideAttrs.ts +1 -0
  140. package/src/runtime/composables/useDragWithThreshold.ts +108 -0
  141. package/src/runtime/composables/usePreHydrationValue.ts +30 -0
  142. package/src/runtime/composables/useSetupI18n.ts +8 -2
  143. package/src/runtime/composables/useSuggestions.ts +92 -45
  144. package/src/runtime/directives/vResizableCols.ts +3 -1
  145. package/src/runtime/helpers/NotificationHandler.ts +5 -0
  146. package/src/runtime/helpers/index.ts +3 -1
  147. package/src/runtime/types/index.ts +5 -0
  148. package/src/runtime/utils/notifyIfError.ts +45 -0
  149. package/dist/runtime/assets/style.css +0 -1
  150. package/dist/runtime/helpers/addValue.d.ts +0 -1
  151. package/dist/runtime/helpers/addValue.js +0 -8
  152. package/src/runtime/helpers/addValue.ts +0 -10
  153. /package/dist/runtime/components/{reset.stories.d.ts → Reset.stories.d.ts} +0 -0
  154. /package/dist/runtime/components/{reset.stories.js → Reset.stories.js} +0 -0
  155. /package/src/runtime/components/{reset.stories.ts → Reset.stories.ts} +0 -0
@@ -1,15 +1,16 @@
1
1
  <template>
2
2
  <div
3
- v-if="suggestions.isOpen"
3
+ v-if="$open"
4
4
  :id="`suggestions-${id ?? fallbackId}`"
5
5
  :class="twMerge(`
6
6
  suggestions
7
7
  bg-bg
8
8
  dark:bg-fg
9
+ dark:text-bg
9
10
  `,
10
11
  ($.attrs as any)?.class
11
12
  )"
12
- :data-open="suggestions.isOpen"
13
+ :data-open="$open"
13
14
  role="listbox"
14
15
  ref="el"
15
16
  v-bind="{...$.attrs, class:undefined}"
@@ -18,12 +19,15 @@
18
19
  <div :id="`suggestion-${id ?? fallbackId}-${index}`"
19
20
  role="option"
20
21
  :class="twMerge(`
21
- px-1
22
- user-select-none
23
- cursor-pointer
24
- px-2
25
- `,
26
- index=== suggestions.active && `bg-accent-200 dark:bg-accent-800`,
22
+ suggestions--item
23
+ user-select-none
24
+ cursor-pointer
25
+ px-2
26
+ `,
27
+ index=== suggestions.active && `
28
+ bg-accent-200
29
+ dark:bg-accent-800/70
30
+ `,
27
31
  ($.itemAttrs as any)?.class
28
32
  )"
29
33
  v-bind="{...$.itemAttrs, class:undefined}"
@@ -33,17 +37,28 @@
33
37
  :key="item"
34
38
  @mouseover="suggestions.active = index"
35
39
  @mousedown.prevent
36
- @mouseup="suggestions.enterSuggestion(index)"
40
+ @mouseup="suggestions.enterIndex(index, !Array.isArray($modelValue))"
37
41
  >
38
- <slot name="item" :item="item" :index="index">
39
- {{ item }}
42
+ <slot name="item"
43
+ :item="item"
44
+ :index="index"
45
+ :is-selected="Array.isArray($modelValue) ? $modelValue.includes(item) : $modelValue === item"
46
+ >
47
+ <div class="flex gap-2 nowrap">
48
+ <lib-checkbox
49
+ v-if="Array.isArray($modelValue) && showSelectedValues"
50
+ :model-value="$modelValue.includes(item)"
51
+ @mousedown.prevent
52
+ />
53
+ <div> {{ item }} </div>
54
+ </div>
40
55
  </slot>
41
56
  </div>
42
57
  </div>
43
58
  </template>
44
59
 
45
60
 
46
- <script setup lang="ts" generic="TSuggestion extends string | object">
61
+ <script setup lang="ts" generic="TSuggestion extends string | object, TValue extends string|string[]">
47
62
 
48
63
  import { type HTMLAttributes,reactive, ref } from "vue"
49
64
 
@@ -51,7 +66,8 @@ import { useDivideAttrs } from "../../composables/useDivideAttrs.js"
51
66
  import { useSuggestions } from "../../composables/useSuggestions.js"
52
67
  import { hasModifiers } from "../../helpers/hasModifiers.js"
53
68
  import { twMerge } from "../../utils/twMerge.js"
54
- import { type BaseInteractiveProps, baseInteractivePropsDefaults, getFallbackId,type LabelProps, type LinkableByIdProps, type MultiValueProps, type SuggestionsEmits, type SuggestionsProps, type WrapperProps } from "../shared/props.js"
69
+ import LibCheckbox from "../LibCheckbox/LibCheckbox.vue"
70
+ import { type BaseInteractiveProps, baseInteractivePropsDefaults, getFallbackId,type LabelProps, type LinkableByIdProps, type SuggestionsEmits, type SuggestionsProps, type WrapperProps } from "../shared/props.js"
55
71
 
56
72
  defineOptions({
57
73
  name: "lib-suggestions",
@@ -67,17 +83,15 @@ const fallbackId = getFallbackId()
67
83
  const props = withDefaults(defineProps<Props & SuggestionsProps<TSuggestion>>(), {
68
84
  isValid: true,
69
85
  canOpen: true,
70
- values: undefined,
71
86
  filterKeydown: undefined,
72
87
  ...baseInteractivePropsDefaults
73
88
  })
74
-
75
89
  /**
76
90
  * The final valid value. This is *not* the value you want to share with the input. If `restrictToSuggestions` is true this will not update on any invalid values that `inputValue` might be set to.
77
91
  *
78
92
  * If suggestions are objects, this will be the string returned by the `suggestionLabel` prop.
79
93
  */
80
- const $modelValue = defineModel<string>("modelValue", { required: true })
94
+ const $modelValue = defineModel<TValue>("modelValue", { required: true })
81
95
  /**
82
96
  * If the element is bound to an input, this is the value that the input should be sharing.
83
97
  *
@@ -85,17 +99,19 @@ const $modelValue = defineModel<string>("modelValue", { required: true })
85
99
  */
86
100
  const $inputValue = defineModel<string >("inputValue", { default: "" })
87
101
 
102
+ const $open = defineModel<boolean>("open", { default: false })
103
+
88
104
 
89
105
  if (typeof props.suggestions?.[0] === "object" && !props.suggestionLabel && !props.suggestionsFilter) {
90
106
  throw new Error("`suggestionLabel` or `suggestionsFilter` must be passed if suggestions are objects.")
91
107
  }
92
108
 
93
109
  const el = ref<HTMLElement | null>(null)
94
- const mousedown = ref(false)
95
110
 
96
- const suggestions = reactive(useSuggestions(
111
+ const suggestions = reactive(useSuggestions<TSuggestion, TValue extends string[] ? true : false>(
97
112
  $inputValue,
98
- $modelValue,
113
+ $modelValue as any,
114
+ $open,
99
115
  emits,
100
116
  props
101
117
  ))
@@ -104,12 +120,12 @@ const inputKeydownHandler = (e: KeyboardEvent): void => {
104
120
  if (props.filterKeydown?.(e)) return
105
121
  if (hasModifiers(e)) return
106
122
  if (e.key === "Enter") {
107
- suggestions.enterSelected()
123
+ suggestions.enterSelected(!Array.isArray($modelValue))
108
124
  e.preventDefault()
109
125
  } else if (e.key === "Escape") {
110
126
  suggestions.cancel()
111
127
  e.preventDefault()
112
- } else if (!suggestions.isOpen && ["ArrowDown", "ArrowUp", "PageUp", "PageDown"].includes(e.key) && suggestions.available) {
128
+ } else if (!$open.value && ["ArrowDown", "ArrowUp", "PageUp", "PageDown"].includes(e.key) && suggestions.available) {
113
129
  suggestions.open()
114
130
  e.preventDefault()
115
131
  if (e.key === "PageUp") {
@@ -134,14 +150,16 @@ const inputKeydownHandler = (e: KeyboardEvent): void => {
134
150
  const inputBlurHandler = (e: MouseEvent): void => {
135
151
  if (props.filterBlur?.(e)) return
136
152
 
137
- if (!suggestions.isOpen) return
153
+ if (!$open.value) return
138
154
 
139
155
  if (props.restrictToSuggestions) {
140
156
  suggestions.cancel()
141
157
  } else {
142
- $modelValue.value = $inputValue.value
158
+ if (!Array.isArray($modelValue.value)) {
159
+ $modelValue.value = $inputValue.value as any
160
+ }
143
161
  }
144
- if (suggestions.isOpen) {
162
+ if ($open.value) {
145
163
  suggestions.close()
146
164
  }
147
165
  }
@@ -173,7 +191,6 @@ type RealProps =
173
191
  & LinkableByIdProps
174
192
  & LabelProps
175
193
  & BaseInteractiveProps
176
- & MultiValueProps
177
194
  & {
178
195
  /** Return true to prevent the keydown event from being handled. */
179
196
  filterKeydown?: (e: KeyboardEvent) => boolean
@@ -40,13 +40,13 @@
40
40
  , ($attrs as any).class)"
41
41
  v-resizable-cols="resizableOptions"
42
42
  >
43
- <thead v-if="header">
44
- <tr>
43
+ <thead v-if="header" class="table--header">
44
+ <tr class="table--row">
45
45
  <template v-for="col,i of cols" :key="col">
46
46
  <slot :name="`header-${col.toString()}`"
47
47
  :class="[
48
48
  extraClasses[`-1-${i}`],
49
- 'cell',
49
+ 'cell table--header-cell',
50
50
  (colConfig as any)[col]?.resizable === false
51
51
  ? 'no-resize'
52
52
  : ''
@@ -56,7 +56,7 @@
56
56
  >
57
57
  <td :class="[
58
58
  extraClasses[`-1-${i}`] ,
59
- 'cell',
59
+ 'cell table--header-cell',
60
60
  (colConfig as any)[col]?.resizable === false
61
61
  ? 'no-resize'
62
62
  : ''
@@ -69,17 +69,17 @@
69
69
  </template>
70
70
  </tr>
71
71
  </thead>
72
- <tbody>
72
+ <tbody class="table--body">
73
73
  <template v-for="item, i of values" :key="typeof itemKey === 'function' ? itemKey(item) : item[itemKey]">
74
- <tr>
74
+ <tr class="table--row">
75
75
  <template v-for="col, j of cols" :key="(typeof itemKey === 'function' ? itemKey(item) : item[itemKey]) + col.toString()">
76
76
  <slot
77
77
  :name="col"
78
78
  :item="item"
79
79
  :value="item[col]"
80
- :class="extraClasses[`${i}-${j}`] + ' cell'"
80
+ :class="extraClasses[`${i}-${j}`] + 'table--cell cell'"
81
81
  >
82
- <td :class="extraClasses[`${i}-${j}`] + ' cell'">
82
+ <td :class="extraClasses[`${i}-${j}`] + 'table--cell cell'">
83
83
  {{ item[col] }}
84
84
  </td>
85
85
  </slot>
@@ -0,0 +1,6 @@
1
+ import type { Meta, StoryObj } from "@storybook/vue3";
2
+ declare const meta: Meta<{}>;
3
+ export default meta;
4
+ type Story = StoryObj<{}>;
5
+ export declare const Scrollbars: Story;
6
+ export declare const TextareaResizer: Story;
@@ -0,0 +1,44 @@
1
+ const meta = {
2
+ title: "Other/Scrolling",
3
+ args: {}
4
+ };
5
+ export default meta;
6
+ export const Scrollbars = {
7
+ render: (args) => ({
8
+ setup: () => ({ args }),
9
+ template: `
10
+ <div
11
+ class="
12
+ relative
13
+ flex
14
+ flex-col
15
+ max-h-[300px]
16
+ max-w-[300px]
17
+ border-2
18
+ border-neutral-500
19
+ "
20
+
21
+ >
22
+ <div
23
+ class="overflow-auto"
24
+ >
25
+ <div class="h-[1000px] w-[1000px]"/>
26
+ </div>
27
+ </div>
28
+ `
29
+ })
30
+ };
31
+ export const TextareaResizer = {
32
+ render: (args) => ({
33
+ setup: () => ({ args }),
34
+ template: `
35
+ <textarea class="
36
+ border-2
37
+ border-neutral-500
38
+ min-w-[200px]
39
+ min-h-[200px]
40
+ [resize:both]
41
+ " />
42
+ `
43
+ })
44
+ };
@@ -10,7 +10,7 @@
10
10
  </template>
11
11
  <script setup lang="ts">
12
12
 
13
- import { computed, getCurrentInstance, type HTMLAttributes, onBeforeUnmount, onMounted, type PropType, reactive, type Ref, ref, watch } from "vue"
13
+ import { computed, type HTMLAttributes, onBeforeUnmount, onMounted, type PropType, reactive, type Ref, ref, watch } from "vue"
14
14
 
15
15
  import { twMerge } from "../../utils/twMerge.js"
16
16
  import { baseInteractiveProps, type TailwindClassProp } from "../shared/props.js"
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <!-- Width 100% + 2xmargin in combination with the margins in LibRoot is a bit of a hack so that it looks like there's padding around the content in test mode (for storybook). We can't just absolutely position these controls or they make the container scroll. -->
3
- <div class="flex gap-2 p-1 pb-10 -ml-10 w-[calc(100%_+_var(--spacing)*20)]">
3
+ <div class="test-controls flex gap-2 p-1 pb-10 -ml-10 w-[calc(100%_+_var(--spacing)*20)]">
4
4
  <div class="flex-grow"/>
5
5
  <div class="outline-indicator">{{ showOutline ? "Outline Enabled" : "Outline Disabled" }}</div>
6
6
  <lib-dark-mode-switcher/>
@@ -1,19 +1,20 @@
1
- export { default as LibTimeZonePicker } from "./LibDatePicker/LibTimeZonePicker.vue.js";
2
1
  export { default as aria } from "./Aria/Aria.vue.js";
3
2
  export { default as LibButton } from "./LibButton/LibButton.vue.js";
3
+ export { default as LibCheckbox } from "./LibCheckbox/LibCheckbox.vue.js";
4
+ export { default as LibColorInput } from "./LibColorInput/LibColorInput.vue.js";
5
+ export { default as LibColorPicker } from "./LibColorPicker/LibColorPicker.vue.js";
6
+ export { default as LibDarkModeSwitcher } from "./LibDarkModeSwitcher/LibDarkModeSwitcher.vue.js";
4
7
  export { default as LibDatePicker } from "./LibDatePicker/LibDatePicker.vue.js";
5
8
  export { default as LibDebug } from "./LibDebug/LibDebug.vue.js";
6
- export { default as LibDarkModeSwitcher } from "./LibDarkModeSwitcher/LibDarkModeSwitcher.vue.js";
7
- export { default as LibInput } from "./LibInput/LibInput.vue.js";
9
+ export { default as LibFileInput } from "./LibFileInput/LibFileInput.vue.js";
10
+ export { default as LibInputDeprecated } from "./LibInputDeprecated/LibInputDeprecated.vue.js";
11
+ export { default as LibLabel } from "./LibLabel/LibLabel.vue.js";
8
12
  export { default as LibNotifications } from "./LibNotifications/LibNotifications.vue.js";
9
13
  export { default as LibPagination } from "./LibPagination/LibPagination.vue.js";
10
- export { default as LibTable } from "./LibTable/LibTable.vue.js";
11
- export { default as LibColorPicker } from "./LibColorPicker/LibColorPicker.vue.js";
12
- export { default as LibColorInput } from "./LibColorInput/LibColorInput.vue.js";
13
14
  export { default as LibPopup } from "./LibPopup/LibPopup.vue.js";
14
- export { default as LibSimpleInput } from "./LibSimpleInput/LibSimpleInput.vue.js";
15
- export { default as LibRecorder } from "./LibRecorder/LibRecorder.vue.js";
16
- export { default as LibLabel } from "./LibLabel/LibLabel.vue.js";
17
- export { default as LibCheckbox } from "./LibCheckbox/LibCheckbox.vue.js";
18
- export { default as LibFileInput } from "./LibFileInput/LibFileInput.vue.js";
19
15
  export { default as LibProgressBar } from "./LibProgressBar/LibProgressBar.vue.js";
16
+ export { default as LibRecorder } from "./LibRecorder/LibRecorder.vue.js";
17
+ export { default as LibRoot } from "./LibRoot/LibRoot.vue.js";
18
+ export { default as LibSimpleInput } from "./LibSimpleInput/LibSimpleInput.vue.js";
19
+ export { default as LibTable } from "./LibTable/LibTable.vue.js";
20
+ export { default as LibTimeZonePicker } from "./LibDatePicker/LibTimeZonePicker.vue.js";
@@ -1,19 +1,20 @@
1
- export { default as LibTimeZonePicker } from "./LibDatePicker/LibTimeZonePicker.vue";
2
1
  export { default as aria } from "./Aria/Aria.vue";
3
2
  export { default as LibButton } from "./LibButton/LibButton.vue";
3
+ export { default as LibCheckbox } from "./LibCheckbox/LibCheckbox.vue";
4
+ export { default as LibColorInput } from "./LibColorInput/LibColorInput.vue";
5
+ export { default as LibColorPicker } from "./LibColorPicker/LibColorPicker.vue";
6
+ export { default as LibDarkModeSwitcher } from "./LibDarkModeSwitcher/LibDarkModeSwitcher.vue";
4
7
  export { default as LibDatePicker } from "./LibDatePicker/LibDatePicker.vue";
5
8
  export { default as LibDebug } from "./LibDebug/LibDebug.vue";
6
- export { default as LibDarkModeSwitcher } from "./LibDarkModeSwitcher/LibDarkModeSwitcher.vue";
7
- export { default as LibInput } from "./LibInput/LibInput.vue";
9
+ export { default as LibFileInput } from "./LibFileInput/LibFileInput.vue";
10
+ export { default as LibInputDeprecated } from "./LibInputDeprecated/LibInputDeprecated.vue";
11
+ export { default as LibLabel } from "./LibLabel/LibLabel.vue";
8
12
  export { default as LibNotifications } from "./LibNotifications/LibNotifications.vue";
9
13
  export { default as LibPagination } from "./LibPagination/LibPagination.vue";
10
- export { default as LibTable } from "./LibTable/LibTable.vue";
11
- export { default as LibColorPicker } from "./LibColorPicker/LibColorPicker.vue";
12
- export { default as LibColorInput } from "./LibColorInput/LibColorInput.vue";
13
14
  export { default as LibPopup } from "./LibPopup/LibPopup.vue";
14
- export { default as LibSimpleInput } from "./LibSimpleInput/LibSimpleInput.vue";
15
- export { default as LibRecorder } from "./LibRecorder/LibRecorder.vue";
16
- export { default as LibLabel } from "./LibLabel/LibLabel.vue";
17
- export { default as LibCheckbox } from "./LibCheckbox/LibCheckbox.vue";
18
- export { default as LibFileInput } from "./LibFileInput/LibFileInput.vue";
19
15
  export { default as LibProgressBar } from "./LibProgressBar/LibProgressBar.vue";
16
+ export { default as LibRecorder } from "./LibRecorder/LibRecorder.vue";
17
+ export { default as LibRoot } from "./LibRoot/LibRoot.vue";
18
+ export { default as LibSimpleInput } from "./LibSimpleInput/LibSimpleInput.vue";
19
+ export { default as LibTable } from "./LibTable/LibTable.vue";
20
+ export { default as LibTimeZonePicker } from "./LibDatePicker/LibTimeZonePicker.vue";
@@ -1,4 +1,5 @@
1
1
  import { type PropType } from "vue";
2
+ import type { PopupPositioner, PopupPositionModifier } from "../../types/index.js";
2
3
  export interface LinkableByIdProps {
3
4
  /**
4
5
  * The id for the input. Uses vue's useId if none provided.
@@ -8,6 +9,11 @@ export interface LinkableByIdProps {
8
9
  id?: string;
9
10
  }
10
11
  export declare const getFallbackId: () => string;
12
+ export interface ButtonProps {
13
+ border?: boolean;
14
+ color?: "warning" | "ok" | "danger" | "primary" | "secondary" | false;
15
+ autoTitleFromAria?: boolean;
16
+ }
11
17
  export interface LabelProps {
12
18
  /** For the label. Note the component might or might not support an actual label element. If none is supported, this is used for the `aria-label`. */
13
19
  label?: string;
@@ -53,6 +59,8 @@ export type SuggestionsProps<T = any> = {
53
59
  allowOpenEmpty?: boolean;
54
60
  /** Whether the suggestions dropdown can be opened. Default is true. */
55
61
  canOpen?: boolean;
62
+ /** Whether the suggestions dropdown can be closed. Default is true. */
63
+ canClose?: boolean;
56
64
  /** Whether the input is valid. Default is true. */
57
65
  isValid?: boolean;
58
66
  /**
@@ -61,12 +69,81 @@ export type SuggestionsProps<T = any> = {
61
69
  * You can implement custom behavior here like fuzzy matching. Note that an exactly matching selection is always picked (the function will not be called).
62
70
  */
63
71
  suggestionSelector?: (suggestions: T[], input: string) => number;
72
+ /** In the case modelValue is an array of values, whether to show the selected values in the suggestions list. Default is true so users can deselect from the list as well. If false, the checkboxes will also not be shown. */
73
+ showSelectedValues?: boolean;
64
74
  };
65
- export type SuggestionsOptions<T> = SuggestionsProps<T> & MultiValueProps;
66
- export interface SuggestionsEmits {
67
- (e: "submit", val: string, suggestion?: any): void;
75
+ export type SuggestionsOptions<T> = SuggestionsProps<T>;
76
+ export interface SuggestionsEmits<TMultivalue extends boolean = false> {
77
+ (e: "submit", val: string, suggestion?: any, wasRemoved?: boolean): void;
68
78
  (e: "update:isOpen", val: boolean): void;
69
- (e: "update:activeSuggestion", val: number): void;
79
+ (e: "update:activeSuggestion", val: TMultivalue extends true ? number[] : number): void;
80
+ }
81
+ export interface PopupProps {
82
+ /**
83
+ * Whether to use the dialog element instead of a regular backdrop. While using the dialog element would be ideal, css variables won't be applied to it, tailwind themes will fail, etc, if the css variables are not applied to `::backdrop`.
84
+ *
85
+ * Using a div ends up easier to setup.
86
+ *
87
+ * The default is now false.
88
+ */
89
+ useDialogForBackdrop?: false;
90
+ /** Wether to use a backdrop (clicking it will close the popup), or not (use is allowed to click elsewhere. */
91
+ useBackdrop?: boolean;
92
+ /**
93
+ * The preferred horizontal positioning of the popup. The first position in the array to fit is used.
94
+ *
95
+ * All elements need to have box-sizing: border-box set. Also note that while the component should work with dynamic popup sizes, in practice there's issues with the positioning being slightly off. Giving the popup element a static size is better. If you need margins around the popup, this can be done with a wrapper element + padding.
96
+ *
97
+ * The positions `right`/`left`/`top`/`bottom` are relative to the opposite side of the button element so as to try not to cover the triggering button.
98
+ *
99
+ * So positioning `right` and `left` look like this:
100
+ *
101
+ * ```
102
+ * // right
103
+ * [button]
104
+ * [----popup----]
105
+ *
106
+ * // left
107
+ * [button]
108
+ * [----popup----]
109
+ * ```
110
+ *
111
+ * Positions `*-most` try to position the popup as close to that side of the screen as possible, otherwise limiting the popup to that edge. For example:
112
+ *
113
+ * ```
114
+ * [--------------screen---------------]
115
+ * // right-most
116
+ * [button]
117
+ * [----popup----]
118
+ * // near the edge:
119
+ * [button]
120
+ * [----popup----]
121
+ * ```
122
+ *
123
+ * There is also the `center-screen` position, which centers the popup on the screen.
124
+ *
125
+ * These last two (`*-most` and `center-screen`) are greedy, they will always find a position that fits. Positions listed after are ignored.
126
+ *
127
+ * You can also specify a function instead which is given some additional information regarding the space around the button reference element. It should a number for the x position (or y, if preferredVertical).
128
+ *
129
+ * If you only need to slightly modify the position, you can use the `modifyPosition` option instead.
130
+ */
131
+ preferredHorizontal?: ("center" | "right" | "left" | "either" | "center-screen" | "right-most" | "left-most" | "center-most")[] | PopupPositioner;
132
+ /** See `preferredHorizontal`. */
133
+ preferredVertical?: ("top" | "bottom" | "center" | "either" | "center-screen" | "top-most" | "bottom-most" | "center-most")[] | PopupPositioner;
134
+ /**
135
+ * When the user scrolls or resizes, normally the entire popup position is recomputed, taking into account the preferred positioning.
136
+ *
137
+ * This can cause it to shift around.
138
+ *
139
+ * Set this to true to only shift the popup depending on how much the button element moved and avoid recalculating the best position.
140
+ */
141
+ avoidRepositioning?: boolean;
142
+ /**
143
+ * Allows modifying the calculated position, to for example, clamp it.
144
+ */
145
+ modifyPosition?: PopupPositionModifier;
146
+ canClose?: boolean;
70
147
  }
71
148
  export declare const baseInteractiveProps: {
72
149
  readonly unstyle: {
@@ -106,18 +183,6 @@ export declare const baseInteractivePropsDefaults: {
106
183
  border: boolean;
107
184
  unstyle: boolean;
108
185
  };
109
- export type MultiValueProps = {
110
- /**
111
- * If values is used, for components that handle multiple values, prevents adding of duplicate values.
112
- *
113
- * For other components, it prevents suggesting values that have already been added.
114
- *
115
- * Default is true.
116
- */
117
- preventDuplicateValues?: boolean;
118
- /** The selected values.*/
119
- values?: string[];
120
- };
121
186
  /**
122
187
  * @internal
123
188
  * For easily typing attributes created by useDivideAttrs. See readme.
@@ -14,13 +14,13 @@ export const playMultipleValues = async ({ canvasElement, args }) => {
14
14
  await userEvent.type(input, "A");
15
15
  await expect(canvas.getByTestId("model-value")).toHaveTextContent("A");
16
16
  await userEvent.keyboard("{Enter}");
17
- await expect(canvas.getByTestId("values").textContent).toBe([...initialValues, "A"].join(", "));
17
+ await expect(canvas.getByTestId("values").textContent).toBe([...initialValues].join(", "));
18
18
  await expect(canvas.getByTestId("model-value")).toBeEmptyDOMElement();
19
- await userEvent.type(input, "B");
20
- await expect(canvas.getByTestId("model-value")).toHaveTextContent("B");
19
+ await userEvent.type(input, "D");
20
+ await expect(canvas.getByTestId("model-value")).toHaveTextContent("D");
21
21
  await userEvent.keyboard("{Enter}");
22
- await expect(canvas.getByTestId("values").textContent).toBe([...initialValues, "A", "B"].join(", "));
22
+ await expect(canvas.getByTestId("values").textContent).toBe([...initialValues, "D"].join(", "));
23
23
  await userEvent.clear(input);
24
24
  await userEvent.keyboard("{Enter}");
25
- await expect(canvas.getByTestId("values").textContent).toBe([...initialValues, "A", "B"].join(", "));
25
+ await expect(canvas.getByTestId("values").textContent).toBe([...initialValues, "D"].join(", "));
26
26
  };
@@ -18,7 +18,7 @@ export const playBasicSelect = async ({ canvasElement, args }) => {
18
18
  await expect(canvas.queryByRole("option", { selected: true })).toBeInTheDocument();
19
19
  await userEvent.clear(input);
20
20
  await userEvent.type(input, "unmatched");
21
- if (!args.suggestionsFilter) {
21
+ if (!args.suggestionsFilter && !args.values) {
22
22
  await expect(canvas.queryAllByRole("option", { selected: true })).toEqual([]);
23
23
  }
24
24
  await userEvent.clear(input);
@@ -32,7 +32,9 @@ export const playBasicSelect = async ({ canvasElement, args }) => {
32
32
  }
33
33
  await userEvent.clear(input);
34
34
  await userEvent.keyboard("AB{Escape}");
35
- await expect(canvas.queryByRole("listbox")).toBeNull();
35
+ if (!args.values) {
36
+ await expect(canvas.queryByRole("listbox")).toBeNull();
37
+ }
36
38
  if (args.values === void 0) {
37
39
  if (args.restrictToSuggestions) {
38
40
  await expect(canvas.getByTestId("model-value").textContent).toBe("A");
@@ -50,16 +52,24 @@ export const playBasicKeyboardSelect = async ({ canvasElement, args }) => {
50
52
  await userEvent.keyboard("{ArrowUp}");
51
53
  await expect(canvas.queryByRole("option", { name: "A", selected: true })).toBeInTheDocument();
52
54
  await userEvent.keyboard("{ArrowUp}");
53
- await expect(canvas.queryByRole("option", { name: "ABCDEFGHIJKLMNOPQRSTUVWXYZ", selected: true })).toBeInTheDocument();
55
+ await expect(canvas.queryByRole("option", {
56
+ name: args.values ? "C" : "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
57
+ selected: true
58
+ })).toBeInTheDocument();
54
59
  await userEvent.keyboard("{ArrowDown}");
55
60
  await expect(canvas.queryByRole("option", { name: "A", selected: true })).toBeInTheDocument();
56
61
  await userEvent.keyboard("{PageDown}");
57
- await expect(canvas.queryByRole("option", { name: "ABCDEFGHIJKLMNOPQRSTUVWXYZ", selected: true })).toBeInTheDocument();
62
+ await expect(canvas.queryByRole("option", {
63
+ name: args.values ? "C" : "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
64
+ selected: true
65
+ })).toBeInTheDocument();
58
66
  await userEvent.keyboard("{PageUp}");
59
67
  await expect(canvas.queryByRole("option", { name: "A", selected: true })).toBeInTheDocument();
60
68
  const testOpen = async (key) => {
61
69
  await userEvent.keyboard("{Escape}");
62
- await expect(canvas.queryByRole("listbox")).toBeNull();
70
+ if (!args.values) {
71
+ await expect(canvas.queryByRole("listbox")).toBeNull();
72
+ }
63
73
  await userEvent.keyboard(`{${key}}`);
64
74
  await expect(canvas.queryByRole("listbox")).toBeInTheDocument();
65
75
  };
@@ -74,10 +84,4 @@ export const playBasicClickSelect = async ({ canvasElement, args }) => {
74
84
  await userEvent.clear(input);
75
85
  await userEvent.type(input, "A");
76
86
  await userEvent.click(canvas.getByRole("option", { name: "AB" }));
77
- if (args.values === void 0) {
78
- await expect(canvas.getByTestId("model-value").textContent).toBe("AB");
79
- await expect(canvas.queryByRole("listbox")).toBeNull();
80
- } else {
81
- await expect(canvas.getByTestId("values")).toHaveTextContent(/AB$/);
82
- }
83
87
  };
@@ -1,10 +1,15 @@
1
+ export { languageLocaleInjectionKey } from "./useSetupLocale.js.js";
1
2
  export { useAccesibilityOutline } from "./useAccesibilityOutline.js.js";
2
3
  export { useAriaLabel } from "./useAriaLabel.js.js";
3
4
  export { useDarkMode } from "./useDarkMode.js.js";
4
5
  export { useDivideAttrs } from "./useDivideAttrs.js.js";
6
+ export { useDragWithThreshold } from "./useDragWithThreshold.js.js";
5
7
  export { useGlobalResizeObserver } from "./useGlobalResizeObserver.js.js";
6
8
  export { useInjectedDarkMode } from "./useInjectedDarkMode.js.js";
9
+ export { useInjectedI18n } from "./useInjectedI18n.js.js";
10
+ export { useInjectedLocale } from "./useInjectedLocale.js.js";
7
11
  export { useNotificationHandler } from "./useNotificationHandler.js.js";
12
+ export { usePreHydrationValue } from "./usePreHydrationValue.js.js";
8
13
  export { useScrollNearContainerEdges } from "./useScrollNearContainerEdges.js.js";
9
14
  export { useSetupDarkMode } from "./useSetupDarkMode.js.js";
10
15
  export { useShowDevOnlyKey } from "./useShowDevOnlyKey.js.js";
@@ -1,10 +1,15 @@
1
+ export { languageLocaleInjectionKey } from "./useSetupLocale.js";
1
2
  export { useAccesibilityOutline } from "./useAccesibilityOutline.js";
2
3
  export { useAriaLabel } from "./useAriaLabel.js";
3
4
  export { useDarkMode } from "./useDarkMode.js";
4
5
  export { useDivideAttrs } from "./useDivideAttrs.js";
6
+ export { useDragWithThreshold } from "./useDragWithThreshold.js";
5
7
  export { useGlobalResizeObserver } from "./useGlobalResizeObserver.js";
6
8
  export { useInjectedDarkMode } from "./useInjectedDarkMode.js";
9
+ export { useInjectedI18n } from "./useInjectedI18n.js";
10
+ export { useInjectedLocale } from "./useInjectedLocale.js";
7
11
  export { useNotificationHandler } from "./useNotificationHandler.js";
12
+ export { usePreHydrationValue } from "./usePreHydrationValue.js";
8
13
  export { useScrollNearContainerEdges } from "./useScrollNearContainerEdges.js";
9
14
  export { useSetupDarkMode } from "./useSetupDarkMode.js";
10
15
  export { useShowDevOnlyKey } from "./useShowDevOnlyKey.js";
@@ -8,6 +8,7 @@ export const useDivideAttrs = (divisionKeys) => computed(() => {
8
8
  res[`${key}Attrs`] = {};
9
9
  for (let i = 0; i < unseen.length; i++) {
10
10
  const attrKey = unseen[i];
11
+ if (!attrKey) continue;
11
12
  if (attrKey.startsWith(`${key}-`)) {
12
13
  res[`${key}Attrs`][attrKey.slice(key.length + 1)] = attrs[attrKey];
13
14
  unseen.splice(i, 1);