reka-ui 2.7.0 → 2.8.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 (272) hide show
  1. package/dist/Avatar/AvatarImage.cjs +4 -2
  2. package/dist/Avatar/AvatarImage.cjs.map +1 -1
  3. package/dist/Avatar/AvatarImage.js +4 -2
  4. package/dist/Avatar/AvatarImage.js.map +1 -1
  5. package/dist/Collection/Collection.cjs +6 -2
  6. package/dist/Collection/Collection.cjs.map +1 -1
  7. package/dist/Collection/Collection.js +6 -2
  8. package/dist/Collection/Collection.js.map +1 -1
  9. package/dist/Combobox/ComboboxContent.cjs +4 -0
  10. package/dist/Combobox/ComboboxContent.cjs.map +1 -1
  11. package/dist/Combobox/ComboboxContent.js +4 -0
  12. package/dist/Combobox/ComboboxContent.js.map +1 -1
  13. package/dist/Combobox/ComboboxContentImpl.cjs +46 -33
  14. package/dist/Combobox/ComboboxContentImpl.cjs.map +1 -1
  15. package/dist/Combobox/ComboboxContentImpl.js +47 -34
  16. package/dist/Combobox/ComboboxContentImpl.js.map +1 -1
  17. package/dist/ContextMenu/ContextMenuContent.cjs +4 -0
  18. package/dist/ContextMenu/ContextMenuContent.cjs.map +1 -1
  19. package/dist/ContextMenu/ContextMenuContent.js +4 -0
  20. package/dist/ContextMenu/ContextMenuContent.js.map +1 -1
  21. package/dist/ContextMenu/ContextMenuRadioGroup.cjs +1 -1
  22. package/dist/ContextMenu/ContextMenuRadioGroup.js +1 -1
  23. package/dist/ContextMenu/ContextMenuRadioItem.cjs +1 -1
  24. package/dist/ContextMenu/ContextMenuRadioItem.js +1 -1
  25. package/dist/ContextMenu/ContextMenuSubContent.cjs +4 -0
  26. package/dist/ContextMenu/ContextMenuSubContent.cjs.map +1 -1
  27. package/dist/ContextMenu/ContextMenuSubContent.js +4 -0
  28. package/dist/ContextMenu/ContextMenuSubContent.js.map +1 -1
  29. package/dist/DatePicker/DatePickerContent.cjs +4 -0
  30. package/dist/DatePicker/DatePickerContent.cjs.map +1 -1
  31. package/dist/DatePicker/DatePickerContent.js +4 -0
  32. package/dist/DatePicker/DatePickerContent.js.map +1 -1
  33. package/dist/DatePicker/DatePickerRoot.cjs +7 -6
  34. package/dist/DatePicker/DatePickerRoot.cjs.map +1 -1
  35. package/dist/DatePicker/DatePickerRoot.js +7 -6
  36. package/dist/DatePicker/DatePickerRoot.js.map +1 -1
  37. package/dist/DateRangePicker/DateRangePickerContent.cjs +4 -0
  38. package/dist/DateRangePicker/DateRangePickerContent.cjs.map +1 -1
  39. package/dist/DateRangePicker/DateRangePickerContent.js +4 -0
  40. package/dist/DateRangePicker/DateRangePickerContent.js.map +1 -1
  41. package/dist/DateRangePicker/DateRangePickerRoot.cjs +7 -6
  42. package/dist/DateRangePicker/DateRangePickerRoot.cjs.map +1 -1
  43. package/dist/DateRangePicker/DateRangePickerRoot.js +7 -6
  44. package/dist/DateRangePicker/DateRangePickerRoot.js.map +1 -1
  45. package/dist/DropdownMenu/DropdownMenuContent.cjs +4 -0
  46. package/dist/DropdownMenu/DropdownMenuContent.cjs.map +1 -1
  47. package/dist/DropdownMenu/DropdownMenuContent.js +4 -0
  48. package/dist/DropdownMenu/DropdownMenuContent.js.map +1 -1
  49. package/dist/DropdownMenu/DropdownMenuRadioGroup.cjs +1 -1
  50. package/dist/DropdownMenu/DropdownMenuRadioGroup.js +1 -1
  51. package/dist/DropdownMenu/DropdownMenuRadioItem.cjs +1 -1
  52. package/dist/DropdownMenu/DropdownMenuRadioItem.js +1 -1
  53. package/dist/DropdownMenu/DropdownMenuSubContent.cjs +4 -0
  54. package/dist/DropdownMenu/DropdownMenuSubContent.cjs.map +1 -1
  55. package/dist/DropdownMenu/DropdownMenuSubContent.js +4 -0
  56. package/dist/DropdownMenu/DropdownMenuSubContent.js.map +1 -1
  57. package/dist/Editable/EditableInput.cjs +1 -1
  58. package/dist/Editable/EditableInput.cjs.map +1 -1
  59. package/dist/Editable/EditableInput.js +1 -1
  60. package/dist/Editable/EditableInput.js.map +1 -1
  61. package/dist/FocusScope/FocusScope.cjs +1 -1
  62. package/dist/FocusScope/FocusScope.cjs.map +1 -1
  63. package/dist/FocusScope/FocusScope.js +2 -2
  64. package/dist/FocusScope/FocusScope.js.map +1 -1
  65. package/dist/FocusScope/stack.cjs +0 -9
  66. package/dist/FocusScope/stack.cjs.map +1 -1
  67. package/dist/FocusScope/stack.js +1 -4
  68. package/dist/FocusScope/stack.js.map +1 -1
  69. package/dist/HoverCard/HoverCardContent.cjs +4 -0
  70. package/dist/HoverCard/HoverCardContent.cjs.map +1 -1
  71. package/dist/HoverCard/HoverCardContent.js +4 -0
  72. package/dist/HoverCard/HoverCardContent.js.map +1 -1
  73. package/dist/HoverCard/HoverCardContentImpl.cjs +4 -0
  74. package/dist/HoverCard/HoverCardContentImpl.cjs.map +1 -1
  75. package/dist/HoverCard/HoverCardContentImpl.js +4 -0
  76. package/dist/HoverCard/HoverCardContentImpl.js.map +1 -1
  77. package/dist/Listbox/ListboxRoot.cjs.map +1 -1
  78. package/dist/Listbox/ListboxRoot.js.map +1 -1
  79. package/dist/Menu/MenuContent.cjs +4 -0
  80. package/dist/Menu/MenuContent.cjs.map +1 -1
  81. package/dist/Menu/MenuContent.js +4 -0
  82. package/dist/Menu/MenuContent.js.map +1 -1
  83. package/dist/Menu/MenuContentImpl.cjs +4 -0
  84. package/dist/Menu/MenuContentImpl.cjs.map +1 -1
  85. package/dist/Menu/MenuContentImpl.js +4 -0
  86. package/dist/Menu/MenuContentImpl.js.map +1 -1
  87. package/dist/Menu/MenuRadioGroup.cjs +1 -1
  88. package/dist/Menu/MenuRadioGroup.cjs.map +1 -1
  89. package/dist/Menu/MenuRadioGroup.js +1 -1
  90. package/dist/Menu/MenuRadioGroup.js.map +1 -1
  91. package/dist/Menu/MenuRadioItem.cjs +1 -1
  92. package/dist/Menu/MenuRadioItem.cjs.map +1 -1
  93. package/dist/Menu/MenuRadioItem.js +1 -1
  94. package/dist/Menu/MenuRadioItem.js.map +1 -1
  95. package/dist/Menu/MenuRootContentModal.cjs +4 -0
  96. package/dist/Menu/MenuRootContentModal.cjs.map +1 -1
  97. package/dist/Menu/MenuRootContentModal.js +4 -0
  98. package/dist/Menu/MenuRootContentModal.js.map +1 -1
  99. package/dist/Menu/MenuRootContentNonModal.cjs +4 -0
  100. package/dist/Menu/MenuRootContentNonModal.cjs.map +1 -1
  101. package/dist/Menu/MenuRootContentNonModal.js +4 -0
  102. package/dist/Menu/MenuRootContentNonModal.js.map +1 -1
  103. package/dist/Menu/MenuSubContent.cjs +4 -0
  104. package/dist/Menu/MenuSubContent.cjs.map +1 -1
  105. package/dist/Menu/MenuSubContent.js +4 -0
  106. package/dist/Menu/MenuSubContent.js.map +1 -1
  107. package/dist/Menubar/MenubarContent.cjs +4 -0
  108. package/dist/Menubar/MenubarContent.cjs.map +1 -1
  109. package/dist/Menubar/MenubarContent.js +4 -0
  110. package/dist/Menubar/MenubarContent.js.map +1 -1
  111. package/dist/Menubar/MenubarRadioGroup.cjs +1 -1
  112. package/dist/Menubar/MenubarRadioGroup.js +1 -1
  113. package/dist/Menubar/MenubarRadioItem.cjs +1 -1
  114. package/dist/Menubar/MenubarRadioItem.js +1 -1
  115. package/dist/Menubar/MenubarSubContent.cjs +4 -0
  116. package/dist/Menubar/MenubarSubContent.cjs.map +1 -1
  117. package/dist/Menubar/MenubarSubContent.js +4 -0
  118. package/dist/Menubar/MenubarSubContent.js.map +1 -1
  119. package/dist/NavigationMenu/NavigationMenuContent.cjs +1 -1
  120. package/dist/NavigationMenu/NavigationMenuContent.js +1 -1
  121. package/dist/NavigationMenu/NavigationMenuContentImpl.cjs +1 -1
  122. package/dist/NavigationMenu/NavigationMenuContentImpl.js +1 -1
  123. package/dist/NavigationMenu/NavigationMenuItem.cjs +1 -1
  124. package/dist/NavigationMenu/NavigationMenuItem.js +1 -1
  125. package/dist/NavigationMenu/NavigationMenuRoot.cjs +7 -5
  126. package/dist/NavigationMenu/NavigationMenuRoot.cjs.map +1 -1
  127. package/dist/NavigationMenu/NavigationMenuRoot.js +8 -6
  128. package/dist/NavigationMenu/NavigationMenuRoot.js.map +1 -1
  129. package/dist/NavigationMenu/NavigationMenuTrigger.cjs +1 -1
  130. package/dist/NavigationMenu/NavigationMenuTrigger.js +1 -1
  131. package/dist/NavigationMenu/NavigationMenuViewport.cjs +1 -1
  132. package/dist/NavigationMenu/NavigationMenuViewport.js +1 -1
  133. package/dist/NumberField/NumberFieldRoot.cjs +6 -1
  134. package/dist/NumberField/NumberFieldRoot.cjs.map +1 -1
  135. package/dist/NumberField/NumberFieldRoot.js +6 -1
  136. package/dist/NumberField/NumberFieldRoot.js.map +1 -1
  137. package/dist/PinInput/PinInputInput.cjs +15 -11
  138. package/dist/PinInput/PinInputInput.cjs.map +1 -1
  139. package/dist/PinInput/PinInputInput.js +15 -11
  140. package/dist/PinInput/PinInputInput.js.map +1 -1
  141. package/dist/Popover/PopoverContent.cjs +4 -0
  142. package/dist/Popover/PopoverContent.cjs.map +1 -1
  143. package/dist/Popover/PopoverContent.js +4 -0
  144. package/dist/Popover/PopoverContent.js.map +1 -1
  145. package/dist/Popover/PopoverContentImpl.cjs +4 -0
  146. package/dist/Popover/PopoverContentImpl.cjs.map +1 -1
  147. package/dist/Popover/PopoverContentImpl.js +4 -0
  148. package/dist/Popover/PopoverContentImpl.js.map +1 -1
  149. package/dist/Popover/PopoverContentModal.cjs +4 -0
  150. package/dist/Popover/PopoverContentModal.cjs.map +1 -1
  151. package/dist/Popover/PopoverContentModal.js +4 -0
  152. package/dist/Popover/PopoverContentModal.js.map +1 -1
  153. package/dist/Popover/PopoverContentNonModal.cjs +4 -0
  154. package/dist/Popover/PopoverContentNonModal.cjs.map +1 -1
  155. package/dist/Popover/PopoverContentNonModal.js +4 -0
  156. package/dist/Popover/PopoverContentNonModal.js.map +1 -1
  157. package/dist/Popper/PopperContent.cjs +10 -2
  158. package/dist/Popper/PopperContent.cjs.map +1 -1
  159. package/dist/Popper/PopperContent.js +10 -2
  160. package/dist/Popper/PopperContent.js.map +1 -1
  161. package/dist/ScrollArea/ScrollAreaRoot.cjs.map +1 -1
  162. package/dist/ScrollArea/ScrollAreaRoot.js.map +1 -1
  163. package/dist/ScrollArea/ScrollAreaScrollbar.cjs +9 -2
  164. package/dist/ScrollArea/ScrollAreaScrollbar.cjs.map +1 -1
  165. package/dist/ScrollArea/ScrollAreaScrollbar.js +9 -2
  166. package/dist/ScrollArea/ScrollAreaScrollbar.js.map +1 -1
  167. package/dist/ScrollArea/ScrollAreaScrollbarGlimpse.cjs +132 -0
  168. package/dist/ScrollArea/ScrollAreaScrollbarGlimpse.cjs.map +1 -0
  169. package/dist/ScrollArea/ScrollAreaScrollbarGlimpse.js +126 -0
  170. package/dist/ScrollArea/ScrollAreaScrollbarGlimpse.js.map +1 -0
  171. package/dist/Select/SelectContent.cjs +4 -0
  172. package/dist/Select/SelectContent.cjs.map +1 -1
  173. package/dist/Select/SelectContent.js +4 -0
  174. package/dist/Select/SelectContent.js.map +1 -1
  175. package/dist/Select/SelectContentImpl.cjs +4 -0
  176. package/dist/Select/SelectContentImpl.cjs.map +1 -1
  177. package/dist/Select/SelectContentImpl.js +4 -0
  178. package/dist/Select/SelectContentImpl.js.map +1 -1
  179. package/dist/Select/SelectPopperPosition.cjs +4 -0
  180. package/dist/Select/SelectPopperPosition.cjs.map +1 -1
  181. package/dist/Select/SelectPopperPosition.js +4 -0
  182. package/dist/Select/SelectPopperPosition.js.map +1 -1
  183. package/dist/Tabs/TabsContent.cjs +4 -0
  184. package/dist/Tabs/TabsContent.cjs.map +1 -1
  185. package/dist/Tabs/TabsContent.js +5 -1
  186. package/dist/Tabs/TabsContent.js.map +1 -1
  187. package/dist/Tabs/TabsRoot.cjs +11 -1
  188. package/dist/Tabs/TabsRoot.cjs.map +1 -1
  189. package/dist/Tabs/TabsRoot.js +12 -2
  190. package/dist/Tabs/TabsRoot.js.map +1 -1
  191. package/dist/Tabs/TabsTrigger.cjs +1 -1
  192. package/dist/Tabs/TabsTrigger.cjs.map +1 -1
  193. package/dist/Tabs/TabsTrigger.js +1 -1
  194. package/dist/Tabs/TabsTrigger.js.map +1 -1
  195. package/dist/TimeField/TimeFieldInput.cjs +3 -1
  196. package/dist/TimeField/TimeFieldInput.cjs.map +1 -1
  197. package/dist/TimeField/TimeFieldInput.js +3 -1
  198. package/dist/TimeField/TimeFieldInput.js.map +1 -1
  199. package/dist/TimeField/TimeFieldRoot.cjs +24 -2
  200. package/dist/TimeField/TimeFieldRoot.cjs.map +1 -1
  201. package/dist/TimeField/TimeFieldRoot.js +24 -2
  202. package/dist/TimeField/TimeFieldRoot.js.map +1 -1
  203. package/dist/constant.d.cts.map +1 -1
  204. package/dist/date/comparators.cjs +14 -2
  205. package/dist/date/comparators.cjs.map +1 -1
  206. package/dist/date/comparators.js +14 -2
  207. package/dist/date/comparators.js.map +1 -1
  208. package/dist/date/parser.cjs +1 -0
  209. package/dist/date/parser.cjs.map +1 -1
  210. package/dist/date/parser.js +1 -0
  211. package/dist/date/parser.js.map +1 -1
  212. package/dist/date/useDateField.cjs +41 -20
  213. package/dist/date/useDateField.cjs.map +1 -1
  214. package/dist/date/useDateField.js +41 -20
  215. package/dist/date/useDateField.js.map +1 -1
  216. package/dist/index.cjs +3 -1
  217. package/dist/index.d.cts +1122 -1085
  218. package/dist/index.d.cts.map +1 -1
  219. package/dist/index.d.ts +1115 -1078
  220. package/dist/index.d.ts.map +1 -1
  221. package/dist/index.js +3 -2
  222. package/dist/index2.d.cts.map +1 -1
  223. package/dist/index2.d.ts.map +1 -1
  224. package/dist/shared/useDateFormatter.cjs +1 -1
  225. package/dist/shared/useDateFormatter.cjs.map +1 -1
  226. package/dist/shared/useDateFormatter.js +1 -1
  227. package/dist/shared/useDateFormatter.js.map +1 -1
  228. package/dist/shared/useGraceArea.cjs +1 -1
  229. package/dist/shared/useGraceArea.cjs.map +1 -1
  230. package/dist/shared/useGraceArea.js +1 -1
  231. package/dist/shared/useGraceArea.js.map +1 -1
  232. package/package.json +5 -5
  233. package/src/Avatar/AvatarImage.vue +2 -1
  234. package/src/Collection/Collection.ts +3 -2
  235. package/src/Combobox/ComboboxContentImpl.vue +39 -32
  236. package/src/Combobox/ComboboxRoot.vue +1 -1
  237. package/src/DatePicker/DatePickerRoot.vue +6 -6
  238. package/src/DatePicker/index.ts +4 -4
  239. package/src/DateRangePicker/DateRangePickerRoot.vue +6 -6
  240. package/src/DateRangePicker/index.ts +4 -4
  241. package/src/Editable/EditableInput.vue +1 -1
  242. package/src/FocusScope/FocusScope.vue +2 -2
  243. package/src/Listbox/ListboxFilter.vue +1 -1
  244. package/src/Listbox/ListboxRoot.vue +2 -1
  245. package/src/Menu/MenuRadioGroup.vue +5 -4
  246. package/src/Menu/MenuRadioItem.vue +2 -1
  247. package/src/NavigationMenu/NavigationMenuRoot.vue +10 -6
  248. package/src/NumberField/NumberFieldRoot.vue +6 -1
  249. package/src/PinInput/PinInputInput.vue +25 -18
  250. package/src/Popper/PopperContent.vue +15 -5
  251. package/src/Rating/RatingItem.vue +52 -0
  252. package/src/Rating/RatingItemIndicator.vue +60 -0
  253. package/src/Rating/RatingRoot.vue +113 -0
  254. package/src/Rating/index.ts +3 -0
  255. package/src/ScrollArea/ScrollAreaRoot.vue +2 -1
  256. package/src/ScrollArea/ScrollAreaScrollbar.vue +9 -0
  257. package/src/ScrollArea/ScrollAreaScrollbarGlimpse.vue +145 -0
  258. package/src/ScrollArea/index.ts +4 -0
  259. package/src/ScrollArea/types.ts +1 -1
  260. package/src/Tabs/TabsContent.vue +6 -1
  261. package/src/Tabs/TabsRoot.vue +14 -1
  262. package/src/Tabs/TabsTrigger.vue +1 -1
  263. package/src/TimeField/TimeFieldInput.vue +6 -1
  264. package/src/TimeField/TimeFieldRoot.vue +25 -2
  265. package/src/Tree/TreeRoot.vue +2 -2
  266. package/src/date/comparators.ts +15 -2
  267. package/src/index.ts +1 -0
  268. package/src/shared/date/parser.ts +10 -0
  269. package/src/shared/date/useDateField.ts +70 -21
  270. package/src/shared/index.ts +1 -1
  271. package/src/shared/useDateFormatter.ts +2 -2
  272. package/src/shared/useGraceArea.ts +1 -1
@@ -12,6 +12,10 @@ export {
12
12
  default as ScrollAreaScrollbar,
13
13
  type ScrollAreaScrollbarProps,
14
14
  } from './ScrollAreaScrollbar.vue'
15
+ export {
16
+ default as ScrollAreaScrollbarGlimpse,
17
+ type ScrollAreaScrollbarGlimpseProps,
18
+ } from './ScrollAreaScrollbarGlimpse.vue'
15
19
  export {
16
20
  default as ScrollAreaThumb,
17
21
  type ScrollAreaThumbProps,
@@ -9,4 +9,4 @@ export interface Sizes {
9
9
  }
10
10
  }
11
11
 
12
- export type ScrollType = 'auto' | 'always' | 'scroll' | 'hover'
12
+ export type ScrollType = 'auto' | 'always' | 'scroll' | 'hover' | 'glimpse'
@@ -15,7 +15,7 @@ export interface TabsContentProps extends PrimitiveProps {
15
15
  </script>
16
16
 
17
17
  <script setup lang="ts">
18
- import { computed, onMounted, ref } from 'vue'
18
+ import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
19
19
  import { Presence } from '@/Presence'
20
20
  import { Primitive } from '@/Primitive'
21
21
  import { injectTabsRootContext } from './TabsRoot.vue'
@@ -33,10 +33,15 @@ const isSelected = computed(() => props.value === rootContext.modelValue.value)
33
33
  const isMountAnimationPreventedRef = ref(isSelected.value)
34
34
 
35
35
  onMounted(() => {
36
+ rootContext.registerContent(props.value)
36
37
  requestAnimationFrame(() => {
37
38
  isMountAnimationPreventedRef.value = false
38
39
  })
39
40
  })
41
+
42
+ onBeforeUnmount(() => {
43
+ rootContext.unregisterContent(props.value)
44
+ })
40
45
  </script>
41
46
 
42
47
  <template>
@@ -14,6 +14,9 @@ export interface TabsRootContext {
14
14
  activationMode: 'automatic' | 'manual'
15
15
  baseId: string
16
16
  tabsList: Ref<HTMLElement | undefined>
17
+ contentIds: Ref<Set<StringOrNumber>>
18
+ registerContent: (value: StringOrNumber) => void
19
+ unregisterContent: (value: StringOrNumber) => void
17
20
  }
18
21
 
19
22
  export interface TabsRootProps<T extends StringOrNumber = StringOrNumber> extends PrimitiveProps {
@@ -55,7 +58,7 @@ export const [injectTabsRootContext, provideTabsRootContext]
55
58
  </script>
56
59
 
57
60
  <script setup lang="ts" generic="T extends StringOrNumber = StringOrNumber">
58
- import { ref, toRefs } from 'vue'
61
+ import { ref, shallowRef, toRefs } from 'vue'
59
62
  import { Primitive } from '@/Primitive'
60
63
 
61
64
  const props = withDefaults(defineProps<TabsRootProps<T>>(), {
@@ -82,6 +85,7 @@ const modelValue = useVModel<TabsRootProps<T>, 'modelValue', 'update:modelValue'
82
85
  })
83
86
 
84
87
  const tabsList = ref<HTMLElement>()
88
+ const contentIds = shallowRef<Set<StringOrNumber>>(new Set())
85
89
 
86
90
  provideTabsRootContext({
87
91
  modelValue,
@@ -94,6 +98,15 @@ provideTabsRootContext({
94
98
  activationMode: props.activationMode,
95
99
  baseId: useId(undefined, 'reka-tabs'),
96
100
  tabsList,
101
+ contentIds,
102
+ registerContent: (value: StringOrNumber) => {
103
+ contentIds.value = new Set([...contentIds.value, value])
104
+ },
105
+ unregisterContent: (value: StringOrNumber) => {
106
+ const newSet = new Set(contentIds.value)
107
+ newSet.delete(value)
108
+ contentIds.value = newSet
109
+ },
97
110
  })
98
111
  </script>
99
112
 
@@ -27,7 +27,7 @@ const { forwardRef } = useForwardExpose()
27
27
  const rootContext = injectTabsRootContext()
28
28
 
29
29
  const triggerId = computed(() => makeTriggerId(rootContext.baseId, props.value))
30
- const contentId = computed(() => makeContentId(rootContext.baseId, props.value))
30
+ const contentId = computed(() => rootContext.contentIds.value.has(props.value) ? makeContentId(rootContext.baseId, props.value) : undefined)
31
31
 
32
32
  const isSelected = computed(() => props.value === rootContext.modelValue.value)
33
33
  </script>
@@ -23,6 +23,7 @@ const lastKeyZero = ref(false)
23
23
  const {
24
24
  handleSegmentClick,
25
25
  handleSegmentKeydown,
26
+ handleSegmentFocusOut,
26
27
  attributes,
27
28
  } = useDateField({
28
29
  hasLeftFocus,
@@ -30,6 +31,7 @@ const {
30
31
  placeholder: rootContext.placeholder,
31
32
  hourCycle: rootContext.hourCycle,
32
33
  step: rootContext.step,
34
+ stepSnapping: rootContext.stepSnapping,
33
35
  segmentValues: rootContext.segmentValues,
34
36
  formatter: rootContext.formatter,
35
37
  part: props.part,
@@ -59,7 +61,10 @@ const isInvalid = computed(() => rootContext.isInvalid.value)
59
61
  v-on="part !== 'literal' ? {
60
62
  mousedown: handleSegmentClick,
61
63
  keydown: handleSegmentKeydown,
62
- focusout: () => { hasLeftFocus = true },
64
+ focusout: () => {
65
+ hasLeftFocus = true
66
+ handleSegmentFocusOut()
67
+ },
63
68
  focusin: (e: FocusEvent) => {
64
69
  rootContext.setFocusedElement(e.target as HTMLElement)
65
70
  },
@@ -32,6 +32,7 @@ type TimeFieldRootContext = {
32
32
  formatter: Formatter
33
33
  hourCycle: HourCycle
34
34
  step: Ref<DateStep>
35
+ stepSnapping: Ref<boolean>
35
36
  segmentValues: Ref<SegmentValueObj>
36
37
  segmentContents: Ref<{ part: SegmentPart, value: string }[]>
37
38
  elements: Ref<Set<HTMLElement>>
@@ -52,6 +53,8 @@ export interface TimeFieldRootProps extends PrimitiveProps, FormFieldProps {
52
53
  hourCycle?: HourCycle
53
54
  /** The stepping interval for the time fields. Defaults to `1`. */
54
55
  step?: DateStep
56
+ /** Whether to enforce snapping the value to the nearest step increment after input. Defaults to `false`. */
57
+ stepSnapping?: boolean
55
58
  /** The granularity to use for formatting times. Defaults to minute if a Time is provided, otherwise defaults to minute. The field will render segments for each part of the date up to and including the specified granularity */
56
59
  granularity?: 'hour' | 'minute' | 'second'
57
60
  /** Whether or not to hide the time zone segment of the field */
@@ -107,6 +110,7 @@ const props = withDefaults(defineProps<TimeFieldRootProps>(), {
107
110
  readonly: false,
108
111
  placeholder: undefined,
109
112
  isDateUnavailable: undefined,
113
+ stepSnapping: false,
110
114
  })
111
115
  const emits = defineEmits<TimeFieldRootEmits>()
112
116
  defineSlots<{
@@ -120,7 +124,7 @@ defineSlots<{
120
124
  }) => any
121
125
  }>()
122
126
 
123
- const { disabled, readonly, granularity, defaultValue, minValue, maxValue, dir: propDir, locale: propLocale } = toRefs(props)
127
+ const { disabled, readonly, granularity, defaultValue, minValue, maxValue, stepSnapping, dir: propDir, locale: propLocale } = toRefs(props)
124
128
  const locale = useLocale(propLocale)
125
129
  const dir = useDirection(propDir)
126
130
 
@@ -218,7 +222,25 @@ const allSegmentContent = computed(() => createContent({
218
222
  isTimeValue: true,
219
223
  }))
220
224
 
221
- const segmentContents = computed(() => allSegmentContent.value.arr)
225
+ const segmentContents = computed(() => {
226
+ const contents = allSegmentContent.value.arr
227
+
228
+ // Convert hour values for 12-hour display
229
+ if (props.hourCycle === 12) {
230
+ return contents.map((segment) => {
231
+ if (segment.part === 'hour' && 'hour' in segmentValues.value) {
232
+ const hour = segmentValues.value.hour
233
+ if (hour !== null) {
234
+ const displayHour = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour
235
+ return { ...segment, value: displayHour.toString() }
236
+ }
237
+ }
238
+ return segment
239
+ })
240
+ }
241
+
242
+ return contents
243
+ })
222
244
 
223
245
  const editableSegmentContents = computed(() => segmentContents.value.filter(({ part }) => part !== 'literal'))
224
246
 
@@ -298,6 +320,7 @@ provideTimeFieldRootContext({
298
320
  formatter,
299
321
  hourCycle: props.hourCycle,
300
322
  step,
323
+ stepSnapping,
301
324
  readonly,
302
325
  segmentValues,
303
326
  isInvalid,
@@ -4,13 +4,13 @@ import { createContext, getActiveElement, useDirection, useSelectionBehavior, us
4
4
  import { flatten } from './utils'
5
5
 
6
6
  export interface TreeRootProps<T = Record<string, any>, U extends Record<string, any> = Record<string, any>, M extends boolean = false> extends PrimitiveProps {
7
- /** The controlled value of the tree. Can be binded with with `v-model`. */
7
+ /** The controlled value of the tree. Can be binded with `v-model`. */
8
8
  modelValue?: M extends true ? U[] : U
9
9
  /** The value of the tree when initially rendered. Use when you do not need to control the state of the tree */
10
10
  defaultValue?: M extends true ? U[] : U
11
11
  /** List of items */
12
12
  items?: T[]
13
- /** The controlled value of the expanded item. Can be binded with with `v-model`. */
13
+ /** The controlled value of the expanded item. Can be binded with `v-model`. */
14
14
  expanded?: string[]
15
15
  /** The value of the expanded tree when initially rendered. Use when you do not need to control the state of the expanded tree */
16
16
  defaultExpanded?: string[]
@@ -143,7 +143,13 @@ export function getLastFirstDayOfWeek<T extends DateValue = DateValue>(
143
143
  firstDayOfWeek: number,
144
144
  locale: string,
145
145
  ): T {
146
- const day = getDayOfWeek(date, locale)
146
+ /**
147
+ * "firstDayOfWeek" is fixed to 0(Sunday) to avoid confusion regarding locales.
148
+ * This also aligns with other date libraries, e.g., date-fns.
149
+ *
150
+ * #see https://github.com/unovue/reka-ui/issues/2157
151
+ */
152
+ const day = getDayOfWeek(date, locale, 'sun')
147
153
 
148
154
  if (firstDayOfWeek > day)
149
155
  return date.subtract({ days: day + 7 - firstDayOfWeek }) as T
@@ -159,7 +165,14 @@ export function getNextLastDayOfWeek<T extends DateValue = DateValue>(
159
165
  firstDayOfWeek: number,
160
166
  locale: string,
161
167
  ): T {
162
- const day = getDayOfWeek(date, locale)
168
+ /**
169
+ * "firstDayOfWeek" is fixed to 0(Sunday) to avoid confusion regarding locales.
170
+ * This also aligns with other date libraries, e.g., date-fns.
171
+ *
172
+ * #see https://github.com/unovue/reka-ui/issues/2157
173
+ */
174
+ const day = getDayOfWeek(date, locale, 'sun')
175
+
163
176
  const lastDayOfWeek = firstDayOfWeek === 0 ? 6 : firstDayOfWeek - 1
164
177
 
165
178
  if (day === lastDayOfWeek)
package/src/index.ts CHANGED
@@ -42,6 +42,7 @@ export {
42
42
  type DateValue,
43
43
  type Formatter,
44
44
  type SegmentPart,
45
+ type TimeValue,
45
46
  useBodyScrollLock,
46
47
  useDateFormatter,
47
48
  useDirection,
@@ -197,6 +197,16 @@ function createContentArr(props: CreateContentArrProps) {
197
197
  if (segment.part === 'timeZoneName' && (!isZonedDateTime(props.dateRef) || hideTimeZone))
198
198
  return false
199
199
 
200
+ // In some locales (e.g., zh-TW), the time zone is represented with square brackets.
201
+ // We also filter out these literals that are just brackets.
202
+ // @see https://github.com/unovue/reka-ui/issues/1670
203
+ if (
204
+ (!isZonedDateTime(props.dateRef) || hideTimeZone)
205
+ && segment.part === 'literal' && ['[', ']'].includes(segment.value.trim())
206
+ ) {
207
+ return false
208
+ }
209
+
200
210
  return true
201
211
  })
202
212
 
@@ -1,10 +1,13 @@
1
- import type { CalendarDateTime, CycleTimeOptions, DateFields, DateValue, TimeFields } from '@internationalized/date'
1
+ import type { CalendarDateTime, DateFields, DateValue, TimeFields } from '@internationalized/date'
2
2
  import type { Ref } from 'vue'
3
3
  import type { AnyExceptLiteral, DateStep, HourCycle, SegmentPart, SegmentValueObj } from './types'
4
4
  import type { Formatter } from '@/shared'
5
+ import {
6
+ DateFormatter,
7
+ } from '@internationalized/date'
5
8
  import { computed } from 'vue'
6
9
  import { getDaysInMonth, toDate } from '@/date'
7
- import { useKbd } from '@/shared'
10
+ import { snapValueToStep, useKbd } from '@/shared'
8
11
  import { isAcceptableSegmentKey, isNumberString, isSegmentNavigationKey } from './segment'
9
12
 
10
13
  type MinuteSecondIncrementProps = {
@@ -19,7 +22,6 @@ type DateTimeValueIncrementation = {
19
22
  part: keyof Omit<DateFields, 'era'> | keyof TimeFields
20
23
  dateRef: DateValue
21
24
  prevValue: number | null
22
- hourCycle?: HourCycle
23
25
  }
24
26
 
25
27
  type SegmentAttrProps = {
@@ -282,6 +284,7 @@ export type UseDateFieldProps = {
282
284
  placeholder: Ref<DateValue>
283
285
  hourCycle: HourCycle
284
286
  step: Ref<DateStep>
287
+ stepSnapping?: Ref<boolean>
285
288
  formatter: Formatter
286
289
  segmentValues: Ref<SegmentValueObj>
287
290
  disabled: Ref<boolean>
@@ -320,7 +323,7 @@ export function useDateField(props: UseDateFieldProps) {
320
323
 
321
324
  return Number.parseInt(str.slice(0, -1))
322
325
  }
323
- function dateTimeValueIncrementation({ e, part, dateRef, prevValue, hourCycle }: DateTimeValueIncrementation): number {
326
+ function dateTimeValueIncrementation({ e, part, dateRef, prevValue }: DateTimeValueIncrementation): number {
324
327
  const step = props.step.value[part] ?? 1
325
328
  const sign = e.key === kbd.ARROW_UP ? step : -step
326
329
 
@@ -328,7 +331,9 @@ export function useDateField(props: UseDateFieldProps) {
328
331
  return dateRef[part as keyof Omit<DateFields, 'era'>]
329
332
 
330
333
  if (part === 'hour' && 'hour' in dateRef) {
331
- const cycleArgs: [keyof DateFields | keyof TimeFields, number, CycleTimeOptions?] = [part, sign, { hourCycle }]
334
+ // Don't pass hourCycle to cycle - internal representation is always 24-hour
335
+ // The hourCycle prop only affects display, not internal cycling
336
+ const cycleArgs: [keyof DateFields | keyof TimeFields, number] = [part, sign]
332
337
  return dateRef.set({ [part as keyof DateValue]: prevValue }).cycle(...cycleArgs)[part]
333
338
  }
334
339
 
@@ -507,8 +512,7 @@ export function useDateField(props: UseDateFieldProps) {
507
512
  return { value: total, moveToNext }
508
513
  }
509
514
 
510
- function updateHour(num: number, prev: number | null) {
511
- const max = 24
515
+ function updateHour(max: number, num: number, prev: number | null) {
512
516
  let moveToNext = false
513
517
  const maxStart = Math.floor(max / 10)
514
518
 
@@ -714,6 +718,11 @@ export function useDateField(props: UseDateFieldProps) {
714
718
  }
715
719
  }
716
720
 
721
+ function uses12HourFormat(locale: string): boolean {
722
+ const hourCycle = new DateFormatter(locale, { hour: 'numeric' }).resolvedOptions().hourCycle
723
+ return hourCycle === 'h11' || hourCycle === 'h12'
724
+ }
725
+
717
726
  function handleHourSegmentKeydown(e: KeyboardEvent) {
718
727
  const dateRef = props.placeholder.value
719
728
  if (!isAcceptableSegmentKey(e.key) || isSegmentNavigationKey(e.key) || !('hour' in dateRef) || !('hour' in props.segmentValues.value))
@@ -721,16 +730,14 @@ export function useDateField(props: UseDateFieldProps) {
721
730
 
722
731
  const prevValue = props.segmentValues.value.hour
723
732
 
724
- const hourCycle = props.hourCycle
725
-
726
733
  if (e.key === kbd.ARROW_UP || e.key === kbd.ARROW_DOWN) {
727
- props.segmentValues.value.hour = dateTimeValueIncrementation({ e, part: 'hour', dateRef: props.placeholder.value, prevValue, hourCycle })
734
+ const newHour = dateTimeValueIncrementation({ e, part: 'hour', dateRef: props.placeholder.value, prevValue })
735
+ props.segmentValues.value.hour = newHour
728
736
 
729
- if ('dayPeriod' in props.segmentValues.value) {
730
- if (props.segmentValues.value.hour < 12)
731
- props.segmentValues.value.dayPeriod = 'AM'
732
- else if (props.segmentValues.value.hour)
733
- props.segmentValues.value.dayPeriod = 'PM'
737
+ if ('dayPeriod' in props.segmentValues.value && newHour !== null) {
738
+ // Determine AM/PM based on internal 24-hour value
739
+ // Hour 0-11 = AM, Hour 12-23 = PM
740
+ props.segmentValues.value.dayPeriod = newHour >= 12 ? 'PM' : 'AM'
734
741
  }
735
742
 
736
743
  return
@@ -738,14 +745,29 @@ export function useDateField(props: UseDateFieldProps) {
738
745
 
739
746
  if (isNumberString(e.key)) {
740
747
  const num = Number.parseInt(e.key)
741
- const { value, moveToNext } = updateHour(num, prevValue)
748
+ const is12Hour = uses12HourFormat(props.formatter.getLocale())
749
+ const max = is12Hour ? 12 : 24
742
750
 
743
- if ('dayPeriod' in props.segmentValues.value && value && value > 12)
744
- props.segmentValues.value.dayPeriod = 'PM'
745
- else if ('dayPeriod' in props.segmentValues.value && value)
746
- props.segmentValues.value.dayPeriod = 'AM'
751
+ let displayPrev = prevValue
752
+ if (is12Hour && prevValue !== null) {
753
+ displayPrev = prevValue === 0 ? 12 : (prevValue > 12 ? prevValue - 12 : prevValue)
754
+ }
747
755
 
748
- props.segmentValues.value.hour = value
756
+ const { value, moveToNext } = updateHour(max, num, displayPrev)
757
+
758
+ // Convert display hour back to internal 24-hour format
759
+ let internalValue = value
760
+ if (is12Hour && value !== null) {
761
+ const period = props.segmentValues.value.dayPeriod || 'AM'
762
+ if (value === 12) {
763
+ internalValue = period === 'AM' ? 0 : 12
764
+ }
765
+ else {
766
+ internalValue = period === 'PM' ? value + 12 : value
767
+ }
768
+ }
769
+
770
+ props.segmentValues.value.hour = internalValue
749
771
 
750
772
  if (moveToNext)
751
773
  props.focusNext()
@@ -881,9 +903,36 @@ export function useDateField(props: UseDateFieldProps) {
881
903
  }
882
904
  }
883
905
 
906
+ function handleSegmentFocusOut() {
907
+ if (!props.stepSnapping?.value)
908
+ return
909
+
910
+ if (props.part === 'hour' && 'hour' in props.segmentValues.value && props.segmentValues.value.hour !== null && props.step.value.hour && props.step.value.hour > 1) {
911
+ props.segmentValues.value.hour = snapValueToStep(props.segmentValues.value.hour, 0, 23, props.step.value.hour)
912
+ if ('dayPeriod' in props.segmentValues.value) {
913
+ if (props.segmentValues.value.hour < 12)
914
+ props.segmentValues.value.dayPeriod = 'AM'
915
+ else if (props.segmentValues.value.hour)
916
+ props.segmentValues.value.dayPeriod = 'PM'
917
+ }
918
+ }
919
+ else if (props.part === 'minute' && 'minute' in props.segmentValues.value && props.segmentValues.value.minute !== null && props.step.value.minute && props.step.value.minute > 1) {
920
+ props.segmentValues.value.minute = snapValueToStep(props.segmentValues.value.minute, 0, 59, props.step.value.minute)
921
+ }
922
+ else if (props.part === 'second' && 'second' in props.segmentValues.value && props.segmentValues.value.second !== null && props.step.value.second && props.step.value.second > 1) {
923
+ props.segmentValues.value.second = snapValueToStep(props.segmentValues.value.second, 0, 59, props.step.value.second)
924
+ }
925
+
926
+ if (Object.values(props.segmentValues.value).every(item => item !== null)) {
927
+ const dateRef = props.placeholder.value.set({ ...props.segmentValues.value as Record<AnyExceptLiteral, number> })
928
+ props.modelValue.value = dateRef.copy()
929
+ }
930
+ }
931
+
884
932
  return {
885
933
  handleSegmentClick,
886
934
  handleSegmentKeydown,
935
+ handleSegmentFocusOut,
887
936
  attributes,
888
937
  }
889
938
  }
@@ -2,7 +2,7 @@ export * from './arrays'
2
2
  export * from './browser'
3
3
  export * from './clamp'
4
4
  export { createContext } from './createContext'
5
- export { type DateRange, type DateStep, type DateValue, type SegmentPart } from './date'
5
+ export { type DateRange, type DateStep, type DateValue, type SegmentPart, type TimeValue } from './date'
6
6
  export { getActiveElement } from './getActiveElement'
7
7
  export { handleAndDispatchCustomEvent } from './handleAndDispatchCustomEvent'
8
8
  export { isValidVNodeElement } from './isValidVNodeElement'
@@ -105,8 +105,8 @@ export function useDateFormatter(initialLocale: string, opts: DateFormatterOptio
105
105
  minute: 'numeric',
106
106
  }).formatToParts(date)
107
107
  const value = parts.find(p => p.type === 'dayPeriod')?.value
108
- // Day period can be "AM"/"PM" or "a.m."/"p.m." in some locales
109
- if (value === 'PM' || value === 'p.m.')
108
+ // Day period can be "AM"/"PM" or "am"/"pm" or "a.m."/"p.m." in some locales
109
+ if (value === 'PM' || value === 'pm' || value === 'p.m.')
110
110
  return 'PM'
111
111
 
112
112
  return 'AM'
@@ -25,7 +25,7 @@ export function useGraceArea(triggerElement: Ref<HTMLElement | undefined>, conta
25
25
  const currentTarget = event.currentTarget as HTMLElement
26
26
  const exitPoint = { x: event.clientX, y: event.clientY }
27
27
  const exitSide = getExitSideFromRect(exitPoint, currentTarget.getBoundingClientRect())
28
- const paddedExitPoints = getPaddedExitPoints(exitPoint, exitSide)
28
+ const paddedExitPoints = getPaddedExitPoints(exitPoint, exitSide, 1)
29
29
  const hoverTargetPoints = getPointsFromRect(hoverTarget.getBoundingClientRect())
30
30
  const graceArea = getHull([...paddedExitPoints, ...hoverTargetPoints])
31
31
  pointerGraceArea.value = graceArea