@personizely/ui 0.0.42

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 (221) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +1 -0
  3. package/dist/personizely-ui.css +6 -0
  4. package/dist/personizely-ui.js +26854 -0
  5. package/dist/personizely-ui.umd.cjs +111 -0
  6. package/package.json +117 -0
  7. package/src/assets/index.css +136 -0
  8. package/src/components/ui/accordion/Accordion.vue +19 -0
  9. package/src/components/ui/accordion/AccordionContent.vue +24 -0
  10. package/src/components/ui/accordion/AccordionItem.vue +37 -0
  11. package/src/components/ui/accordion/AccordionTrigger.vue +39 -0
  12. package/src/components/ui/accordion/index.ts +2 -0
  13. package/src/components/ui/alert/Alert.vue +68 -0
  14. package/src/components/ui/alert/index.ts +22 -0
  15. package/src/components/ui/alert-dialog/AlertDialog.vue +66 -0
  16. package/src/components/ui/alert-dialog/AlertDialogContent.vue +44 -0
  17. package/src/components/ui/alert-dialog/AlertDialogProvider.vue +27 -0
  18. package/src/components/ui/alert-dialog/AlertDialogTrigger.vue +11 -0
  19. package/src/components/ui/alert-dialog/index.ts +3 -0
  20. package/src/components/ui/alert-dialog/useAlertDialog.ts +19 -0
  21. package/src/components/ui/autocomplete/Autocomplete.vue +170 -0
  22. package/src/components/ui/autocomplete/AutocompleteContent.vue +27 -0
  23. package/src/components/ui/autocomplete/AutocompleteEmpty.vue +20 -0
  24. package/src/components/ui/autocomplete/AutocompleteGroup.vue +29 -0
  25. package/src/components/ui/autocomplete/AutocompleteItem.vue +26 -0
  26. package/src/components/ui/autocomplete/AutocompleteRoot.vue +31 -0
  27. package/src/components/ui/autocomplete/AutocompleteSeparator.vue +23 -0
  28. package/src/components/ui/autocomplete/index.ts +1 -0
  29. package/src/components/ui/avatar/Avatar.vue +31 -0
  30. package/src/components/ui/avatar/AvatarFallback.vue +11 -0
  31. package/src/components/ui/avatar/AvatarImage.vue +9 -0
  32. package/src/components/ui/avatar/index.ts +19 -0
  33. package/src/components/ui/badge/Badge.vue +16 -0
  34. package/src/components/ui/badge/index.ts +22 -0
  35. package/src/components/ui/button/Button.vue +123 -0
  36. package/src/components/ui/button/index.ts +78 -0
  37. package/src/components/ui/calendar/Calendar.vue +76 -0
  38. package/src/components/ui/calendar/CalendarCell.vue +24 -0
  39. package/src/components/ui/calendar/CalendarCellTrigger.vue +38 -0
  40. package/src/components/ui/calendar/CalendarGrid.vue +24 -0
  41. package/src/components/ui/calendar/CalendarGridBody.vue +11 -0
  42. package/src/components/ui/calendar/CalendarGridHead.vue +11 -0
  43. package/src/components/ui/calendar/CalendarGridRow.vue +21 -0
  44. package/src/components/ui/calendar/CalendarHeadCell.vue +21 -0
  45. package/src/components/ui/calendar/CalendarHeader.vue +21 -0
  46. package/src/components/ui/calendar/CalendarHeading.vue +27 -0
  47. package/src/components/ui/calendar/CalendarNextButton.vue +32 -0
  48. package/src/components/ui/calendar/CalendarPrevButton.vue +32 -0
  49. package/src/components/ui/calendar/index.ts +1 -0
  50. package/src/components/ui/card/Card.vue +57 -0
  51. package/src/components/ui/card/CardContent.vue +14 -0
  52. package/src/components/ui/card/CardDescription.vue +14 -0
  53. package/src/components/ui/card/CardFooter.vue +14 -0
  54. package/src/components/ui/card/CardHeader.vue +14 -0
  55. package/src/components/ui/card/CardTitle.vue +18 -0
  56. package/src/components/ui/card/CardTray.vue +14 -0
  57. package/src/components/ui/card/index.ts +1 -0
  58. package/src/components/ui/checkbox/Checkbox.vue +63 -0
  59. package/src/components/ui/checkbox/CheckboxBase.vue +39 -0
  60. package/src/components/ui/checkbox/index.ts +1 -0
  61. package/src/components/ui/checkbox-group/CheckboxGroup.vue +65 -0
  62. package/src/components/ui/checkbox-group/index.ts +15 -0
  63. package/src/components/ui/color-picker/Alpha.vue +63 -0
  64. package/src/components/ui/color-picker/Angle.vue +145 -0
  65. package/src/components/ui/color-picker/Checkboard.vue +43 -0
  66. package/src/components/ui/color-picker/Color.vue +255 -0
  67. package/src/components/ui/color-picker/ColorPicker.vue +25 -0
  68. package/src/components/ui/color-picker/Gradient.vue +172 -0
  69. package/src/components/ui/color-picker/Handle.vue +19 -0
  70. package/src/components/ui/color-picker/Hue.vue +80 -0
  71. package/src/components/ui/color-picker/LabelInput.vue +16 -0
  72. package/src/components/ui/color-picker/Rail.vue +100 -0
  73. package/src/components/ui/color-picker/Saturation.vue +142 -0
  74. package/src/components/ui/color-picker/index.ts +4 -0
  75. package/src/components/ui/combobox/Combobox.vue +202 -0
  76. package/src/components/ui/combobox/ComboboxContent.vue +27 -0
  77. package/src/components/ui/combobox/ComboboxEmpty.vue +20 -0
  78. package/src/components/ui/combobox/ComboboxGroup.vue +29 -0
  79. package/src/components/ui/combobox/ComboboxInput.vue +52 -0
  80. package/src/components/ui/combobox/ComboboxItem.vue +26 -0
  81. package/src/components/ui/combobox/ComboboxRoot.vue +31 -0
  82. package/src/components/ui/combobox/ComboboxSeparator.vue +23 -0
  83. package/src/components/ui/combobox/index.ts +1 -0
  84. package/src/components/ui/date-picker/DatePicker.vue +55 -0
  85. package/src/components/ui/date-picker/index.ts +1 -0
  86. package/src/components/ui/date-range-picker/DateRangePicker.vue +137 -0
  87. package/src/components/ui/date-range-picker/index.ts +1 -0
  88. package/src/components/ui/dialog/Dialog.vue +78 -0
  89. package/src/components/ui/dialog/DialogContent.vue +46 -0
  90. package/src/components/ui/dialog/DialogDescription.vue +24 -0
  91. package/src/components/ui/dialog/DialogFooter.vue +19 -0
  92. package/src/components/ui/dialog/DialogHeader.vue +16 -0
  93. package/src/components/ui/dialog/DialogTitle.vue +29 -0
  94. package/src/components/ui/dialog/DialogTrigger.vue +11 -0
  95. package/src/components/ui/dialog/index.ts +1 -0
  96. package/src/components/ui/drawer/Drawer.vue +88 -0
  97. package/src/components/ui/drawer/DrawerContent.vue +57 -0
  98. package/src/components/ui/drawer/DrawerDescription.vue +22 -0
  99. package/src/components/ui/drawer/DrawerFooter.vue +19 -0
  100. package/src/components/ui/drawer/DrawerHeader.vue +16 -0
  101. package/src/components/ui/drawer/DrawerTitle.vue +22 -0
  102. package/src/components/ui/drawer/DrawerTrigger.vue +11 -0
  103. package/src/components/ui/drawer/index.ts +21 -0
  104. package/src/components/ui/dropdown-menu/DropdownCheckboxGroupMenu.vue +87 -0
  105. package/src/components/ui/dropdown-menu/DropdownMenu.vue +72 -0
  106. package/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +40 -0
  107. package/src/components/ui/dropdown-menu/DropdownMenuContent.vue +37 -0
  108. package/src/components/ui/dropdown-menu/DropdownMenuGroup.vue +11 -0
  109. package/src/components/ui/dropdown-menu/DropdownMenuHelp.vue +14 -0
  110. package/src/components/ui/dropdown-menu/DropdownMenuItem.vue +28 -0
  111. package/src/components/ui/dropdown-menu/DropdownMenuLabel.vue +24 -0
  112. package/src/components/ui/dropdown-menu/DropdownMenuPart.vue +64 -0
  113. package/src/components/ui/dropdown-menu/DropdownMenuPartItem.vue +76 -0
  114. package/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +19 -0
  115. package/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +41 -0
  116. package/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue +22 -0
  117. package/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue +14 -0
  118. package/src/components/ui/dropdown-menu/DropdownMenuSub.vue +19 -0
  119. package/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue +30 -0
  120. package/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +33 -0
  121. package/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue +13 -0
  122. package/src/components/ui/dropdown-menu/DropdownRadioGroupMenu.vue +75 -0
  123. package/src/components/ui/dropdown-menu/index.ts +36 -0
  124. package/src/components/ui/file-upload-button/FileUploadButton.vue +55 -0
  125. package/src/components/ui/file-upload-button/index.ts +1 -0
  126. package/src/components/ui/form/Form.vue +13 -0
  127. package/src/components/ui/form/FormControl.vue +16 -0
  128. package/src/components/ui/form/FormDescription.vue +20 -0
  129. package/src/components/ui/form/FormField.vue +61 -0
  130. package/src/components/ui/form/FormLabel.vue +23 -0
  131. package/src/components/ui/form/FormMessage.vue +16 -0
  132. package/src/components/ui/form/index.ts +2 -0
  133. package/src/components/ui/form/useFormField.ts +30 -0
  134. package/src/components/ui/icon/Icon.vue +16 -0
  135. package/src/components/ui/icon/index.ts +1 -0
  136. package/src/components/ui/input/Input.vue +51 -0
  137. package/src/components/ui/input/InputBase.vue +18 -0
  138. package/src/components/ui/input/index.ts +1 -0
  139. package/src/components/ui/label/Label.vue +27 -0
  140. package/src/components/ui/label/index.ts +1 -0
  141. package/src/components/ui/pagination/Pagination.vue +50 -0
  142. package/src/components/ui/pagination/PaginationContent.vue +21 -0
  143. package/src/components/ui/pagination/PaginationEllipsis.vue +24 -0
  144. package/src/components/ui/pagination/PaginationFirst.vue +32 -0
  145. package/src/components/ui/pagination/PaginationItem.vue +32 -0
  146. package/src/components/ui/pagination/PaginationLast.vue +32 -0
  147. package/src/components/ui/pagination/PaginationNext.vue +32 -0
  148. package/src/components/ui/pagination/PaginationPrevious.vue +32 -0
  149. package/src/components/ui/pagination/index.ts +1 -0
  150. package/src/components/ui/popover/Popover.vue +57 -0
  151. package/src/components/ui/popover/PopoverTrigger.vue +15 -0
  152. package/src/components/ui/popover/index.ts +1 -0
  153. package/src/components/ui/progress/Progress.vue +35 -0
  154. package/src/components/ui/progress/ProgressIndicator.vue +19 -0
  155. package/src/components/ui/progress/index.ts +2 -0
  156. package/src/components/ui/progress-circular/ProgressCircular.vue +85 -0
  157. package/src/components/ui/progress-circular/index.ts +1 -0
  158. package/src/components/ui/radio-group/RadioGroup.vue +81 -0
  159. package/src/components/ui/radio-group/RadioGroupItem.vue +39 -0
  160. package/src/components/ui/radio-group/index.ts +15 -0
  161. package/src/components/ui/range-calendar/RangeCalendar.vue +71 -0
  162. package/src/components/ui/range-calendar/RangeCalendarCell.vue +28 -0
  163. package/src/components/ui/range-calendar/RangeCalendarCellTrigger.vue +40 -0
  164. package/src/components/ui/range-calendar/RangeCalendarGrid.vue +24 -0
  165. package/src/components/ui/range-calendar/RangeCalendarGridBody.vue +11 -0
  166. package/src/components/ui/range-calendar/RangeCalendarGridHead.vue +11 -0
  167. package/src/components/ui/range-calendar/RangeCalendarGridRow.vue +21 -0
  168. package/src/components/ui/range-calendar/RangeCalendarHeadCell.vue +21 -0
  169. package/src/components/ui/range-calendar/RangeCalendarHeader.vue +21 -0
  170. package/src/components/ui/range-calendar/RangeCalendarHeading.vue +27 -0
  171. package/src/components/ui/range-calendar/RangeCalendarNextButton.vue +32 -0
  172. package/src/components/ui/range-calendar/RangeCalendarPrevButton.vue +32 -0
  173. package/src/components/ui/range-calendar/index.ts +1 -0
  174. package/src/components/ui/select/Select.vue +120 -0
  175. package/src/components/ui/select/SelectContent.vue +45 -0
  176. package/src/components/ui/select/SelectGroup.vue +19 -0
  177. package/src/components/ui/select/SelectItem.vue +43 -0
  178. package/src/components/ui/select/SelectLabel.vue +13 -0
  179. package/src/components/ui/select/SelectSeparator.vue +17 -0
  180. package/src/components/ui/select/SelectTrigger.vue +31 -0
  181. package/src/components/ui/select/SelectValue.vue +11 -0
  182. package/src/components/ui/select/index.ts +1 -0
  183. package/src/components/ui/slider/Slider.vue +100 -0
  184. package/src/components/ui/slider/index.ts +1 -0
  185. package/src/components/ui/switch/Switch.vue +40 -0
  186. package/src/components/ui/switch/SwitchBase.vue +36 -0
  187. package/src/components/ui/switch/index.ts +1 -0
  188. package/src/components/ui/tabs/Tabs.vue +63 -0
  189. package/src/components/ui/tabs/TabsContent.vue +26 -0
  190. package/src/components/ui/tabs/TabsTrigger.vue +27 -0
  191. package/src/components/ui/tabs/index.ts +28 -0
  192. package/src/components/ui/textarea/Textarea.vue +13 -0
  193. package/src/components/ui/textarea/index.ts +1 -0
  194. package/src/components/ui/toast/Toast.vue +28 -0
  195. package/src/components/ui/toast/ToastAction.vue +26 -0
  196. package/src/components/ui/toast/ToastClose.vue +29 -0
  197. package/src/components/ui/toast/ToastDescription.vue +19 -0
  198. package/src/components/ui/toast/ToastProvider.vue +11 -0
  199. package/src/components/ui/toast/ToastTitle.vue +19 -0
  200. package/src/components/ui/toast/ToastViewport.vue +17 -0
  201. package/src/components/ui/toast/Toaster.vue +30 -0
  202. package/src/components/ui/toast/index.ts +34 -0
  203. package/src/components/ui/toast/useToast.ts +163 -0
  204. package/src/components/ui/toggle/Toggle.vue +51 -0
  205. package/src/components/ui/toggle/index.ts +71 -0
  206. package/src/components/ui/toggle-group/ToggleGroup.vue +58 -0
  207. package/src/components/ui/toggle-group/ToggleGroupItem.vue +54 -0
  208. package/src/components/ui/toggle-group/index.ts +1 -0
  209. package/src/components/ui/tooltip/Tooltip.vue +42 -0
  210. package/src/components/ui/tooltip/TooltipProvider.vue +11 -0
  211. package/src/components/ui/tooltip/index.ts +2 -0
  212. package/src/composables/delegated-props.ts +6 -0
  213. package/src/composables/emits-as-props.ts +17 -0
  214. package/src/composables/forward-props-emits.ts +11 -0
  215. package/src/directives/autofocus.ts +7 -0
  216. package/src/index.ts +159 -0
  217. package/src/options-provider.ts +19 -0
  218. package/src/utils/gradient.ts +158 -0
  219. package/src/utils/options.ts +40 -0
  220. package/src/utils/tailwind.ts +14 -0
  221. package/web-types.json +10560 -0
@@ -0,0 +1,202 @@
1
+ <template>
2
+ <ComboboxRoot
3
+ v-bind="forwarded"
4
+ v-model="modelValue"
5
+ v-model:open="open"
6
+ :reset-search-term-on-blur="false"
7
+ @update:open="onToggle"
8
+ >
9
+ <ComboboxAnchor as-child>
10
+ <ComboboxTrigger
11
+ ref="button"
12
+ role="combobox"
13
+ :aria-disabled="disabled"
14
+ :aria-expanded="open"
15
+ :tabindex="null"
16
+ :class="cn('flex gap-1.5 h-8 text-left items-center w-full justify-between rounded-md border border-input bg-background px-2 py-1 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1 [&>span]:break-all',
17
+ (multiple && Array.isArray(modelValue) && modelValue.length === 0) || !modelValue ? 'text-muted-foreground' : '',
18
+ props.class
19
+ )"
20
+ @focus="$emit('focus', $event)"
21
+ @blur="$emit('blur', $event)"
22
+ >
23
+ <span class="pointer-events-none">
24
+ <slot v-if="multiple && Array.isArray(modelValue) && (modelValue.length || $slots.label)" name="label" v-bind="{ options: selectedOptions }">
25
+ {{ selectedOptionsLabel || modelValue!.length + ' selected' }}
26
+ </slot>
27
+ <slot v-else-if="!multiple && (modelValue || $slots.label)" name="label" v-bind="{ option: selectedOption }">
28
+ {{ selectedOptionLabel || modelValue }}
29
+ </slot>
30
+ <template v-else>
31
+ {{ placeholder }}
32
+ </template>
33
+ </span>
34
+ <ChevronDown class="w-4 h-4 min-w-4 opacity-50" />
35
+ </ComboboxTrigger>
36
+ </ComboboxAnchor>
37
+
38
+ <ComboboxPortal :disabled="disablePortal" :to="opts.portalTo">
39
+ <ComboboxContent :side-offset="5">
40
+ <ComboboxInput
41
+ class="border-b"
42
+ :model-value="searchTerm"
43
+ :placeholder="searchPlaceholder"
44
+ :loading="loading"
45
+ :display-value="() => ''"
46
+ @update:model-value="(v) => {
47
+ if (v !== searchTerm) {
48
+ searchTerm = v
49
+ }
50
+ }"
51
+ @change.stop
52
+ @input.stop
53
+ @keydown.tab.prevent
54
+ />
55
+ <ComboboxViewport class="max-h-[300px] overflow-y-auto overflow-x-hidden">
56
+ <ComboboxEmpty />
57
+
58
+ <ComboboxGroup class="p-1 empty:p-0">
59
+ <ComboboxItem
60
+ v-for="(option, index) in preparedOptions"
61
+ :key="option[keys.id] || option[keys.value] || index"
62
+ :value="option[keys.value]"
63
+ :disabled="option[keys.disabled] || disabled"
64
+ @select="onSelect($event, option)"
65
+ >
66
+ <slot name="option" v-bind="{ option }">
67
+ {{ option[keys.label] || option }}
68
+ </slot>
69
+ <ComboboxItemIndicator as-child>
70
+ <Check class="ml-auto h-4 w-4 text-muted-foreground" />
71
+ </ComboboxItemIndicator>
72
+ </ComboboxItem>
73
+ </ComboboxGroup>
74
+ </ComboboxViewport>
75
+ </ComboboxContent>
76
+ </ComboboxPortal>
77
+ </ComboboxRoot>
78
+ </template>
79
+
80
+ <script setup lang="ts">
81
+ import { type HTMLAttributes, computed, ref, type ComponentInstance } from 'vue'
82
+ import {
83
+ ComboboxItemIndicator,
84
+ ComboboxAnchor,
85
+ ComboboxPortal,
86
+ ComboboxTrigger,
87
+ ComboboxViewport,
88
+ type ComboboxItemEmits,
89
+ type ComboboxRootEmits,
90
+ type ComboboxRootProps,
91
+ type ComboboxInputProps
92
+ } from 'reka-ui'
93
+ import { cn } from '@/utils/tailwind'
94
+ import ComboboxRoot from './ComboboxRoot.vue'
95
+ import ComboboxContent from './ComboboxContent.vue'
96
+ import ComboboxItem from './ComboboxItem.vue'
97
+ import ComboboxInput from './ComboboxInput.vue'
98
+ import ComboboxEmpty from './ComboboxEmpty.vue'
99
+ import ComboboxGroup from './ComboboxGroup.vue'
100
+ import { Check } from 'lucide-vue-next'
101
+ import { ChevronDown } from 'lucide-vue-next'
102
+ import { type CustomOption, type Keys, type Option, prepareOptions } from '@/utils/options'
103
+ import { useDelegatedProps } from '@/composables/delegated-props'
104
+ import { useEmitAsProps } from '@/composables/emits-as-props'
105
+ import { useForwardPropsEmits } from '@/composables/forward-props-emits'
106
+ import { getOptions } from '@/options-provider'
107
+
108
+ const modelValue = defineModel<ComboboxRootProps['modelValue']>()
109
+ const searchTerm = defineModel<ComboboxInputProps['modelValue']>('searchTerm', { default: '' })
110
+
111
+ const props = withDefaults(defineProps<Omit<ComboboxRootProps, 'modelValue' | 'resetSearchTermOnBlur'> & {
112
+ class?: HTMLAttributes['class']
113
+ placeholder?: string
114
+ loading?: boolean
115
+ disablePortal?: boolean
116
+ searchPlaceholder?: string
117
+ keys?: Keys
118
+ options: string[] | Option[] | CustomOption[] | { [key:string]: string }
119
+ }>(), {
120
+ disablePortal: false,
121
+ loading: false,
122
+ placeholder: 'Select a value...',
123
+ searchPlaceholder: 'Search...',
124
+ keys: () => ({
125
+ id: 'id',
126
+ label: 'label',
127
+ help: 'help',
128
+ value: 'value',
129
+ disabled: 'disabled'
130
+ })
131
+ })
132
+
133
+ const emits = defineEmits<Omit<ComboboxRootEmits, 'update:modelValue'> & {
134
+ blur: [event: MouseEvent]
135
+ focus: [event: MouseEvent]
136
+ select: [option: Option | CustomOption],
137
+ 'update:searchTerm': [value: string]
138
+ }>()
139
+
140
+ const delegatedProps = useDelegatedProps(props, ['class', 'placeholder', 'searchPlaceholder', 'keys', 'options', 'disablePortal', 'searchTerm', 'modelValue'])
141
+ const delegatedEmits = useEmitAsProps(emits, ['blur', 'focus', 'select', 'update:searchTerm', 'update:modelValue'])
142
+ const forwarded = useForwardPropsEmits(delegatedProps, delegatedEmits)
143
+ const opts = getOptions()
144
+
145
+ defineSlots<{
146
+ 'label'(props: { option: CustomOption | undefined | null } | { options: Array<Option | CustomOption> }): any
147
+ 'option'(props: { option: Option | CustomOption }): any
148
+ }>()
149
+
150
+ const button = ref<ComponentInstance<typeof ComboboxTrigger>>()
151
+ const open = ref(false)
152
+
153
+ const onSelect = (_event: ComboboxItemEmits['select'][0], option: Option | CustomOption) => {
154
+ emits('select', option)
155
+ }
156
+
157
+ const onToggle = (open: boolean) => {
158
+ requestAnimationFrame(() => {
159
+ if (!open) {
160
+ button.value?.$el.focus()
161
+ }
162
+ })
163
+ }
164
+
165
+ const selectedOption = computed(() => {
166
+ if (modelValue.value) {
167
+ return preparedOptions.value.find((o) => {
168
+ return o[props.keys.value] === modelValue.value
169
+ })
170
+ }
171
+
172
+ return null
173
+ })
174
+
175
+ const selectedOptions = computed(() => {
176
+ if (Array.isArray(modelValue.value)) {
177
+ return preparedOptions.value.filter((o) => {
178
+ return (<Array<unknown>>modelValue.value).includes(o[props.keys.value])
179
+ })
180
+ }
181
+
182
+ return []
183
+ })
184
+
185
+ const selectedOptionLabel = computed(() => {
186
+ if (selectedOption.value) {
187
+ return selectedOption.value[props.keys.label]
188
+ }
189
+
190
+ return null
191
+ })
192
+
193
+ const selectedOptionsLabel = computed(() => {
194
+ if (selectedOptions.value) {
195
+ return selectedOptions.value.map(o => o[props.keys.label]).join(', ')
196
+ }
197
+
198
+ return null
199
+ })
200
+
201
+ const preparedOptions = computed(() => prepareOptions(props.options, props.keys))
202
+ </script>
@@ -0,0 +1,27 @@
1
+ <template>
2
+ <ComboboxContent v-bind="forwarded" :class="cn('z-50 min-w-(--reka-popper-anchor-width) rounded-md border bg-popover text-popover-foreground shadow-md outline-hidden data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', props.class)">
3
+ <div role="presentation">
4
+ <slot />
5
+ </div>
6
+ </ComboboxContent>
7
+ </template>
8
+
9
+ <script setup lang="ts">
10
+ import { type HTMLAttributes, computed } from 'vue'
11
+ import type { ComboboxContentEmits, ComboboxContentProps } from 'reka-ui'
12
+ import { ComboboxContent, useForwardPropsEmits } from 'reka-ui'
13
+ import { cn } from '@/utils/tailwind'
14
+
15
+ const props = withDefaults(defineProps<ComboboxContentProps & { class?: HTMLAttributes['class'] }>(), {
16
+ position: 'popper'
17
+ })
18
+ const emits = defineEmits<ComboboxContentEmits>()
19
+
20
+ const delegatedProps = computed(() => {
21
+ const { class: _, ...delegated } = props
22
+
23
+ return delegated
24
+ })
25
+
26
+ const forwarded = useForwardPropsEmits(delegatedProps, emits)
27
+ </script>
@@ -0,0 +1,20 @@
1
+ <template>
2
+ <ComboboxEmpty v-bind="delegatedProps" :class="cn('py-4 text-center text-sm', props.class)">
3
+ <slot />
4
+ </ComboboxEmpty>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { type HTMLAttributes, computed } from 'vue'
9
+ import type { ComboboxEmptyProps } from 'reka-ui'
10
+ import { ComboboxEmpty } from 'reka-ui'
11
+ import { cn } from '@/utils/tailwind'
12
+
13
+ const props = defineProps<ComboboxEmptyProps & { class?: HTMLAttributes['class'] }>()
14
+
15
+ const delegatedProps = computed(() => {
16
+ const { class: _, ...delegated } = props
17
+
18
+ return delegated
19
+ })
20
+ </script>
@@ -0,0 +1,29 @@
1
+ <template>
2
+ <SelectGroup
3
+ v-bind="delegatedProps"
4
+ :class="cn('overflow-hidden text-foreground', props.class)"
5
+ >
6
+ <SelectLabel v-if="heading" class="px-2 py-1.5 text-xs font-medium text-muted-foreground">
7
+ {{ heading }}
8
+ </SelectLabel>
9
+ <slot />
10
+ </SelectGroup>
11
+ </template>
12
+
13
+ <script setup lang="ts">
14
+ import { type HTMLAttributes, computed } from 'vue'
15
+ import type { ComboboxGroupProps } from 'reka-ui'
16
+ import { SelectGroup, SelectLabel } from 'reka-ui'
17
+ import { cn } from '@/utils/tailwind'
18
+
19
+ const props = defineProps<ComboboxGroupProps & {
20
+ class?: HTMLAttributes['class']
21
+ heading?: string
22
+ }>()
23
+
24
+ const delegatedProps = computed(() => {
25
+ const { class: _, ...delegated } = props
26
+
27
+ return delegated
28
+ })
29
+ </script>
@@ -0,0 +1,52 @@
1
+ <template>
2
+ <div class="flex items-center relative">
3
+ <span class="absolute start-0 inset-y-0 flex items-center justify-center px-2 pointer-events-none">
4
+ <Search class="size-4 text-muted-foreground" />
5
+ </span>
6
+ <ComboboxInput
7
+ v-bind="{ ...forwardedProps, ...$attrs }"
8
+ auto-focus
9
+ role="searchbox"
10
+ :aria-expanded="null"
11
+ size="10"
12
+ :class="cn(
13
+ 'flex h-8 w-full ps-8 bg-transparent py-1 text-sm outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50',
14
+ props.class,
15
+ loading ? 'pe-8' : 'pe-2'
16
+ )"
17
+ />
18
+ <span v-if="loading" class="absolute end-0 inset-y-0 flex items-center justify-center px-2 pointer-events-none">
19
+ <ProgressCircular class="size-4 text-muted-foreground" />
20
+ </span>
21
+ </div>
22
+ </template>
23
+
24
+ <script setup lang="ts">
25
+ import { type HTMLAttributes, computed } from 'vue'
26
+ import { Search } from 'lucide-vue-next'
27
+ import { ComboboxInput, type ComboboxInputProps, useForwardProps } from 'reka-ui'
28
+ import { cn } from '@/utils/tailwind'
29
+ import { ProgressCircular } from '@/components/ui/progress-circular'
30
+
31
+ defineOptions({
32
+ inheritAttrs: false
33
+ })
34
+
35
+ const props = withDefaults(defineProps<ComboboxInputProps & {
36
+ class?: HTMLAttributes['class'],
37
+ loading: boolean
38
+ }>(), {
39
+ loading: false
40
+ })
41
+
42
+ const delegatedProps = computed(() => {
43
+ const { class: _, ...delegated } = props
44
+
45
+ return delegated
46
+ })
47
+
48
+ const forwardedProps = useForwardProps(delegatedProps)
49
+ </script>
50
+
51
+
52
+
@@ -0,0 +1,26 @@
1
+ <template>
2
+ <ComboboxItem
3
+ v-bind="forwarded"
4
+ :class="cn('relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-hidden data-highlighted:bg-accent aria-selected:font-medium data-disabled:pointer-events-none data-disabled:opacity-50', props.class)"
5
+ >
6
+ <slot />
7
+ </ComboboxItem>
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ import { type HTMLAttributes, computed } from 'vue'
12
+ import type { ComboboxItemEmits, ComboboxItemProps } from 'reka-ui'
13
+ import { ComboboxItem, useForwardPropsEmits } from 'reka-ui'
14
+ import { cn } from '@/utils/tailwind'
15
+
16
+ const props = defineProps<ComboboxItemProps & { class?: HTMLAttributes['class'] }>()
17
+ const emits = defineEmits<ComboboxItemEmits>()
18
+
19
+ const delegatedProps = computed(() => {
20
+ const { class: _, ...delegated } = props
21
+
22
+ return delegated
23
+ })
24
+
25
+ const forwarded = useForwardPropsEmits(delegatedProps, emits)
26
+ </script>
@@ -0,0 +1,31 @@
1
+ <template>
2
+ <ComboboxRoot
3
+ v-bind="forwarded"
4
+ :class="cn(props.class)"
5
+ as-child
6
+ >
7
+ <slot />
8
+ </ComboboxRoot>
9
+ </template>
10
+
11
+ <script setup lang="ts">
12
+ import { type HTMLAttributes, computed } from 'vue'
13
+ import type { ComboboxRootEmits, ComboboxRootProps } from 'reka-ui'
14
+ import { ComboboxRoot, useForwardPropsEmits } from 'reka-ui'
15
+ import { cn } from '@/utils/tailwind'
16
+
17
+ const props = withDefaults(defineProps<ComboboxRootProps & { class?: HTMLAttributes['class'] }>(), {
18
+ open: true,
19
+ modelValue: ''
20
+ })
21
+
22
+ const emits = defineEmits<ComboboxRootEmits>()
23
+
24
+ const delegatedProps = computed(() => {
25
+ const { class: _, ...delegated } = props
26
+
27
+ return delegated
28
+ })
29
+
30
+ const forwarded = useForwardPropsEmits(delegatedProps, emits)
31
+ </script>
@@ -0,0 +1,23 @@
1
+ <template>
2
+ <ComboboxSeparator
3
+ v-bind="delegatedProps"
4
+ :class="cn('-mx-1 h-px bg-border', props.class)"
5
+ >
6
+ <slot />
7
+ </ComboboxSeparator>
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ import { type HTMLAttributes, computed } from 'vue'
12
+ import type { ComboboxSeparatorProps } from 'reka-ui'
13
+ import { ComboboxSeparator } from 'reka-ui'
14
+ import { cn } from '@/utils/tailwind'
15
+
16
+ const props = defineProps<ComboboxSeparatorProps & { class?: HTMLAttributes['class'] }>()
17
+
18
+ const delegatedProps = computed(() => {
19
+ const { class: _, ...delegated } = props
20
+
21
+ return delegated
22
+ })
23
+ </script>
@@ -0,0 +1 @@
1
+ export { default as Combobox } from './Combobox.vue'
@@ -0,0 +1,55 @@
1
+ <template>
2
+ <Popover class="p-0">
3
+ <template #trigger>
4
+ <Button
5
+ variant="outline"
6
+ :class="cn(
7
+ 'justify-start text-left font-normal',
8
+ !modelValue && 'text-muted-foreground',
9
+ props.class
10
+ )"
11
+ >
12
+ <template #icon>
13
+ <CalendarIcon class="size-3 min-w-3" />
14
+ </template>
15
+ <slot v-if="modelValue" name="label">
16
+ <span class="truncate">{{ formatter.format(modelValue.toDate(getLocalTimeZone())) }}</span>
17
+ </slot>
18
+ <template v-else>
19
+ <span class="truncate">{{ placeholder }}</span>
20
+ </template>
21
+ </Button>
22
+ </template>
23
+ <Calendar
24
+ v-model="modelValue"
25
+ :multiple="false"
26
+ initial-focus
27
+ />
28
+ </Popover>
29
+ </template>
30
+
31
+ <script setup lang="ts">
32
+ import {
33
+ DateFormatter,
34
+ type DateValue,
35
+ getLocalTimeZone
36
+ } from '@internationalized/date'
37
+ import { Calendar as CalendarIcon } from 'lucide-vue-next'
38
+ import { Calendar } from '@/components/ui/calendar'
39
+ import { Button } from '@/components/ui/button'
40
+ import { Popover } from '@/components/ui/popover'
41
+ import { cn } from '@/utils/tailwind'
42
+ import { type HTMLAttributes } from 'vue'
43
+
44
+ const modelValue = defineModel<DateValue>()
45
+ const props = withDefaults(defineProps<{
46
+ placeholder?: string
47
+ class?: HTMLAttributes['class']
48
+ formatter?: DateFormatter
49
+ }>(), {
50
+ placeholder: 'Pick a date',
51
+ formatter: () => new DateFormatter('en-US', {
52
+ dateStyle: 'long'
53
+ })
54
+ })
55
+ </script>
@@ -0,0 +1 @@
1
+ export { default as DatePicker } from './DatePicker.vue'
@@ -0,0 +1,137 @@
1
+ <template>
2
+ <Popover v-model:open="isOpen" class="p-0">
3
+ <template #trigger>
4
+ <Button
5
+ variant="outline"
6
+ :class="cn(
7
+ 'justify-start text-left font-normal',
8
+ !modelValue.start && 'text-muted-foreground',
9
+ props.class
10
+ )"
11
+ >
12
+ <template #icon>
13
+ <CalendarIcon class="size-3 min-w-3" />
14
+ </template>
15
+ <template v-if="modelValue.start">
16
+ <span class="truncate">
17
+ <template v-if="modelValue.end">
18
+ {{ formatter.format(modelValue.start.toDate(getLocalTimeZone())) }} - {{ formatter.format(modelValue.end.toDate(getLocalTimeZone())) }}
19
+ </template>
20
+
21
+ <template v-else>
22
+ {{ formatter.format(modelValue.start.toDate(getLocalTimeZone())) }}
23
+ </template>
24
+ </span>
25
+ </template>
26
+ <template v-else>
27
+ <span class="truncate">{{ placeholder }}</span>
28
+ </template>
29
+ </Button>
30
+ </template>
31
+ <div class="flex">
32
+ <div v-if="presets.length > 0" class="p-3 flex flex-col gap-1">
33
+ <Button
34
+ v-for="preset in presets"
35
+ :key="preset.label"
36
+ size="sm"
37
+ :aria-selected="isSelectedPreset(preset.value)"
38
+ :variant="isSelectedPreset(preset.value) ? 'secondary' : 'ghost'"
39
+ class="justify-start min-w-28"
40
+ @click="dateRange = preset.value"
41
+ >
42
+ {{ preset.label }}
43
+ </Button>
44
+ </div>
45
+ <RangeCalendar
46
+ v-bind="forwarded"
47
+ v-model="dateRange"
48
+ :number-of-months="2"
49
+ :prevent-deselect="true"
50
+ />
51
+ </div>
52
+ <div class="p-3 flex justify-end gap-2">
53
+ <Button variant="ghost" size="sm" @click="reset">
54
+ Reset
55
+ </Button>
56
+ <Button :disabled="!dateRange.start && !dateRange.end" size="sm" @click="apply">
57
+ Apply
58
+ </Button>
59
+ </div>
60
+ </Popover>
61
+ </template>
62
+
63
+ <script setup lang="ts">
64
+ import {
65
+ DateFormatter,
66
+ getLocalTimeZone
67
+ } from '@internationalized/date'
68
+ import {
69
+ type DateRange,
70
+ type RangeCalendarRootProps
71
+ } from 'reka-ui'
72
+ import { Calendar as CalendarIcon } from 'lucide-vue-next'
73
+ import { RangeCalendar } from '@/components/ui/range-calendar'
74
+ import { Button } from '@/components/ui/button'
75
+ import { Popover } from '@/components/ui/popover'
76
+ import { cn } from '@/utils/tailwind'
77
+ import { type HTMLAttributes, type Ref, ref, watch } from 'vue'
78
+ import { useDelegatedProps } from '@/composables/delegated-props'
79
+ import { useEmitAsProps } from '@/composables/emits-as-props'
80
+ import { useForwardPropsEmits } from '@/composables/forward-props-emits'
81
+
82
+ const emit = defineEmits(['update:modelValue'])
83
+ const props = withDefaults(defineProps<Omit<RangeCalendarRootProps, 'placeholder' | 'modelValue'> & {
84
+ placeholder?: string
85
+ formatter?: DateFormatter
86
+ class?: HTMLAttributes['class']
87
+ modelValue: DateRange
88
+ presets?: Array<{ label: string, value: DateRange }>
89
+ }>(), {
90
+ placeholder: 'Pick a date range',
91
+ initialFocus: false,
92
+ defaultValue: () => ({ start: undefined, end: undefined }),
93
+ numberOfMonths: 2,
94
+ presets: () => [],
95
+ formatter: () => new DateFormatter('en-US', {
96
+ dateStyle: 'long'
97
+ })
98
+ })
99
+
100
+ const delegatedProps = useDelegatedProps(props, ['class', 'placeholder', 'formatter', 'presets', 'modelValue'])
101
+ const delegatedEmits = useEmitAsProps(emit, ['update:modelValue'])
102
+ const forwarded = useForwardPropsEmits(delegatedProps, delegatedEmits)
103
+
104
+ const isOpen = ref(false)
105
+
106
+ const dateRange = ref({
107
+ start: props.modelValue.start,
108
+ end: props.modelValue.end
109
+ }) as Ref<DateRange>
110
+
111
+ watch(() => props.modelValue, (to) => {
112
+ dateRange.value = {
113
+ start: to ? to.start : undefined,
114
+ end: to ? to.end : undefined
115
+ }
116
+ })
117
+
118
+ const reset = () => {
119
+ emit('update:modelValue', props.defaultValue)
120
+ isOpen.value = false
121
+ }
122
+
123
+ const apply = () => {
124
+ emit('update:modelValue', dateRange.value)
125
+ isOpen.value = false
126
+ }
127
+
128
+ const isSelectedPreset = (value: DateRange) => {
129
+ const { end, start } = dateRange.value
130
+ return start && value.start
131
+ ? start.compare(value.start) === 0
132
+ : start === value.start
133
+ && end && value.end
134
+ ? end.compare(value.end) === 0
135
+ : end === value.end
136
+ }
137
+ </script>
@@ -0,0 +1 @@
1
+ export { default as DateRangePicker } from './DateRangePicker.vue'