@human-kit/svelte-components 1.0.0-alpha.2 → 1.0.0-alpha.4

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 (217) hide show
  1. package/dist/FOCUS_STATE_CONTRACT.md +63 -0
  2. package/dist/FOCUS_STATE_REVIEW_TEMPLATE.md +70 -0
  3. package/dist/calendar/README.md +2 -1
  4. package/dist/calendar/TODO.md +21 -107
  5. package/dist/calendar/body-cell/README.md +15 -0
  6. package/dist/calendar/body-cell/calendar-body-cell.svelte +116 -41
  7. package/dist/calendar/grid/README.md +13 -0
  8. package/dist/calendar/grid-body/README.md +13 -0
  9. package/dist/calendar/grid-header/README.md +13 -0
  10. package/dist/calendar/header-cell/README.md +14 -0
  11. package/dist/calendar/heading/README.md +13 -0
  12. package/dist/calendar/root/README.md +24 -0
  13. package/dist/calendar/root/calendar-root-test.svelte +4 -0
  14. package/dist/calendar/root/calendar-root-test.svelte.d.ts +1 -0
  15. package/dist/calendar/root/calendar-root.svelte +3 -0
  16. package/dist/calendar/root/calendar-root.svelte.d.ts +1 -0
  17. package/dist/calendar/root/context.d.ts +4 -0
  18. package/dist/calendar/root/context.js +28 -25
  19. package/dist/calendar/root/date-utils.d.ts +1 -1
  20. package/dist/calendar/root/date-utils.js +16 -26
  21. package/dist/calendar/trigger-next/README.md +14 -0
  22. package/dist/calendar/trigger-previous/README.md +14 -0
  23. package/dist/clock/README.md +75 -0
  24. package/dist/clock/axis/README.md +24 -0
  25. package/dist/clock/axis/clock-axis.svelte +37 -0
  26. package/dist/clock/axis/clock-axis.svelte.d.ts +8 -0
  27. package/dist/clock/hooks/use-wheel-scroll.svelte.d.ts +16 -0
  28. package/dist/clock/hooks/use-wheel-scroll.svelte.js +336 -0
  29. package/dist/clock/index.d.ts +10 -0
  30. package/dist/clock/index.js +10 -0
  31. package/dist/clock/index.parts.d.ts +4 -0
  32. package/dist/clock/index.parts.js +4 -0
  33. package/dist/clock/root/README.md +38 -0
  34. package/dist/clock/root/clock-root-test.svelte +62 -0
  35. package/dist/clock/root/clock-root-test.svelte.d.ts +14 -0
  36. package/dist/clock/root/clock-root.svelte +329 -0
  37. package/dist/clock/root/clock-root.svelte.d.ts +25 -0
  38. package/dist/clock/root/context.d.ts +22 -0
  39. package/dist/clock/root/context.js +15 -0
  40. package/dist/clock/root/resolve-visible-columns.d.ts +7 -0
  41. package/dist/clock/root/resolve-visible-columns.js +16 -0
  42. package/dist/clock/root/time-utils.d.ts +48 -0
  43. package/dist/clock/root/time-utils.js +314 -0
  44. package/dist/clock/root/wheel-options.d.ts +17 -0
  45. package/dist/clock/root/wheel-options.js +63 -0
  46. package/dist/clock/wheel-column/README.md +25 -0
  47. package/dist/clock/wheel-column/clock-wheel-column-bindable-test.svelte +16 -0
  48. package/dist/clock/wheel-column/clock-wheel-column-bindable-test.svelte.d.ts +3 -0
  49. package/dist/clock/wheel-column/clock-wheel-column-custom-snippet-test.svelte +29 -0
  50. package/dist/clock/wheel-column/clock-wheel-column-custom-snippet-test.svelte.d.ts +6 -0
  51. package/dist/clock/wheel-column/clock-wheel-column-default-height-test.svelte +11 -0
  52. package/dist/clock/wheel-column/clock-wheel-column-default-height-test.svelte.d.ts +3 -0
  53. package/dist/clock/wheel-column/clock-wheel-column-test.svelte +38 -0
  54. package/dist/clock/wheel-column/clock-wheel-column-test.svelte.d.ts +12 -0
  55. package/dist/clock/wheel-column/clock-wheel-column-tp-test.svelte +38 -0
  56. package/dist/clock/wheel-column/clock-wheel-column-tp-test.svelte.d.ts +12 -0
  57. package/dist/clock/wheel-column/clock-wheel-column-untagged-snippet-test.svelte +29 -0
  58. package/dist/clock/wheel-column/clock-wheel-column-untagged-snippet-test.svelte.d.ts +6 -0
  59. package/dist/clock/wheel-column/clock-wheel-column.svelte +499 -0
  60. package/dist/clock/wheel-column/clock-wheel-column.svelte.d.ts +17 -0
  61. package/dist/clock/wheel-item/README.md +17 -0
  62. package/dist/clock/wheel-item/clock-wheel-item.svelte +49 -0
  63. package/dist/clock/wheel-item/clock-wheel-item.svelte.d.ts +17 -0
  64. package/dist/combobox/TODO.md +28 -175
  65. package/dist/combobox/button/combobox-button.svelte +2 -0
  66. package/dist/combobox/root/combobox.svelte +30 -0
  67. package/dist/datepicker/README.md +100 -0
  68. package/dist/datepicker/TODO.md +28 -0
  69. package/dist/datepicker/calendar/README.md +19 -0
  70. package/dist/datepicker/calendar/date-picker-calendar-unsafe-props-test.svelte +60 -0
  71. package/dist/datepicker/calendar/date-picker-calendar-unsafe-props-test.svelte.d.ts +3 -0
  72. package/dist/datepicker/calendar/date-picker-calendar.svelte +65 -0
  73. package/dist/datepicker/calendar/date-picker-calendar.svelte.d.ts +10 -0
  74. package/dist/datepicker/index.d.ts +18 -0
  75. package/dist/datepicker/index.js +18 -0
  76. package/dist/datepicker/index.parts.d.ts +14 -0
  77. package/dist/datepicker/index.parts.js +14 -0
  78. package/dist/datepicker/input/README.md +15 -0
  79. package/dist/datepicker/input/date-picker-input.svelte +108 -0
  80. package/dist/datepicker/input/date-picker-input.svelte.d.ts +11 -0
  81. package/dist/datepicker/internal/strict-props.d.ts +2 -0
  82. package/dist/datepicker/internal/strict-props.js +28 -0
  83. package/dist/datepicker/popover/README.md +20 -0
  84. package/dist/datepicker/popover/date-picker-popover-handler-test.svelte +57 -0
  85. package/dist/datepicker/popover/date-picker-popover-handler-test.svelte.d.ts +3 -0
  86. package/dist/datepicker/popover/date-picker-popover-unsafe-props-test.svelte +45 -0
  87. package/dist/datepicker/popover/date-picker-popover-unsafe-props-test.svelte.d.ts +18 -0
  88. package/dist/datepicker/popover/date-picker-popover.svelte +87 -0
  89. package/dist/datepicker/popover/date-picker-popover.svelte.d.ts +7 -0
  90. package/dist/datepicker/root/README.md +38 -0
  91. package/dist/datepicker/root/context.d.ts +43 -0
  92. package/dist/datepicker/root/context.js +15 -0
  93. package/dist/datepicker/root/date-picker-bindable-empty-test.svelte +24 -0
  94. package/dist/datepicker/root/date-picker-bindable-empty-test.svelte.d.ts +3 -0
  95. package/dist/datepicker/root/date-picker-bindable-test.svelte +41 -0
  96. package/dist/datepicker/root/date-picker-bindable-test.svelte.d.ts +3 -0
  97. package/dist/datepicker/root/date-picker-empty-test.svelte +47 -0
  98. package/dist/datepicker/root/date-picker-empty-test.svelte.d.ts +3 -0
  99. package/dist/datepicker/root/date-picker-locale-typing-test.svelte +47 -0
  100. package/dist/datepicker/root/date-picker-locale-typing-test.svelte.d.ts +3 -0
  101. package/dist/datepicker/root/date-picker-open-cancel-test.svelte +54 -0
  102. package/dist/datepicker/root/date-picker-open-cancel-test.svelte.d.ts +8 -0
  103. package/dist/datepicker/root/date-picker-root.svelte +495 -0
  104. package/dist/datepicker/root/date-picker-root.svelte.d.ts +24 -0
  105. package/dist/datepicker/root/date-picker-test.svelte +86 -0
  106. package/dist/datepicker/root/date-picker-test.svelte.d.ts +13 -0
  107. package/dist/datepicker/root/date-utils.d.ts +17 -0
  108. package/dist/datepicker/root/date-utils.js +138 -0
  109. package/dist/datepicker/root/draft-evaluation.d.ts +13 -0
  110. package/dist/datepicker/root/draft-evaluation.js +56 -0
  111. package/dist/datepicker/root/focus-controller.d.ts +3 -0
  112. package/dist/datepicker/root/focus-controller.js +15 -0
  113. package/dist/datepicker/root/open-change.d.ts +5 -0
  114. package/dist/datepicker/root/open-change.js +13 -0
  115. package/dist/datepicker/root/open-controller.d.ts +7 -0
  116. package/dist/datepicker/root/open-controller.js +15 -0
  117. package/dist/datepicker/root/segment-controller.d.ts +8 -0
  118. package/dist/datepicker/root/segment-controller.js +53 -0
  119. package/dist/datepicker/root/segment-state.d.ts +18 -0
  120. package/dist/datepicker/root/segment-state.js +134 -0
  121. package/dist/datepicker/root/value-commit.d.ts +4 -0
  122. package/dist/datepicker/root/value-commit.js +8 -0
  123. package/dist/datepicker/segment/README.md +14 -0
  124. package/dist/datepicker/segment/date-picker-segment.svelte +319 -0
  125. package/dist/datepicker/segment/date-picker-segment.svelte.d.ts +9 -0
  126. package/dist/datepicker/trigger/README.md +14 -0
  127. package/dist/datepicker/trigger/date-picker-trigger.svelte +110 -0
  128. package/dist/datepicker/trigger/date-picker-trigger.svelte.d.ts +9 -0
  129. package/dist/dialog/content/dialog-content.svelte +6 -6
  130. package/dist/dialog/root/context.d.ts +2 -1
  131. package/dist/dialog/root/dialog-root.svelte +9 -2
  132. package/dist/index.d.ts +8 -0
  133. package/dist/index.js +8 -0
  134. package/dist/listbox/root/listbox.svelte +44 -0
  135. package/dist/popover/README.md +10 -0
  136. package/dist/popover/content/popover-content-standalone-test.svelte +28 -0
  137. package/dist/popover/content/popover-content-standalone-test.svelte.d.ts +6 -0
  138. package/dist/popover/content/popover-content-test.svelte +2 -1
  139. package/dist/popover/content/popover-content-test.svelte.d.ts +2 -1
  140. package/dist/popover/content/popover-content.svelte +91 -18
  141. package/dist/popover/content/popover-content.svelte.d.ts +5 -1
  142. package/dist/popover/index.d.ts +1 -1
  143. package/dist/popover/index.js +1 -3
  144. package/dist/popover/root/README.md +10 -15
  145. package/dist/popover/root/context.d.ts +16 -7
  146. package/dist/popover/root/context.js +0 -2
  147. package/dist/popover/root/focus-state.d.ts +4 -0
  148. package/dist/popover/root/focus-state.js +33 -0
  149. package/dist/popover/root/popover-root.svelte +90 -17
  150. package/dist/popover/root/popover-root.svelte.d.ts +2 -1
  151. package/dist/popover/root/popover-test.svelte +2 -1
  152. package/dist/popover/root/popover-test.svelte.d.ts +2 -1
  153. package/dist/popover/trigger/popover-trigger-button.svelte +4 -4
  154. package/dist/popover/trigger/popover-trigger.svelte +1 -1
  155. package/dist/portal/portal.svelte +3 -1
  156. package/dist/primitives/click-outside.d.ts +1 -1
  157. package/dist/primitives/click-outside.js +1 -1
  158. package/dist/primitives/focus-trap.d.ts +7 -2
  159. package/dist/primitives/focus-trap.js +50 -17
  160. package/dist/primitives/index.d.ts +1 -0
  161. package/dist/primitives/index.js +1 -0
  162. package/dist/primitives/input-modality.d.ts +7 -0
  163. package/dist/primitives/input-modality.js +125 -0
  164. package/dist/test-utils/focus-contract.d.ts +3 -0
  165. package/dist/test-utils/focus-contract.js +26 -0
  166. package/dist/timepicker/IMPLEMENTATION_PLAN.md +254 -0
  167. package/dist/timepicker/README.md +97 -0
  168. package/dist/timepicker/TODO.md +86 -0
  169. package/dist/timepicker/clock/README.md +14 -0
  170. package/dist/timepicker/clock/time-picker-clock-test.svelte +45 -0
  171. package/dist/timepicker/clock/time-picker-clock-test.svelte.d.ts +11 -0
  172. package/dist/timepicker/clock/time-picker-clock.svelte +65 -0
  173. package/dist/timepicker/clock/time-picker-clock.svelte.d.ts +10 -0
  174. package/dist/timepicker/index.d.ts +14 -0
  175. package/dist/timepicker/index.js +14 -0
  176. package/dist/timepicker/index.parts.d.ts +8 -0
  177. package/dist/timepicker/index.parts.js +8 -0
  178. package/dist/timepicker/input/README.md +15 -0
  179. package/dist/timepicker/input/time-picker-input-forwarding-test.svelte +40 -0
  180. package/dist/timepicker/input/time-picker-input-forwarding-test.svelte.d.ts +3 -0
  181. package/dist/timepicker/input/time-picker-input.svelte +109 -0
  182. package/dist/timepicker/input/time-picker-input.svelte.d.ts +11 -0
  183. package/dist/timepicker/internal/strict-props.d.ts +4 -0
  184. package/dist/timepicker/internal/strict-props.js +51 -0
  185. package/dist/timepicker/popover/README.md +20 -0
  186. package/dist/timepicker/popover/time-picker-popover-unsafe-props-test.svelte +22 -0
  187. package/dist/timepicker/popover/time-picker-popover-unsafe-props-test.svelte.d.ts +3 -0
  188. package/dist/timepicker/popover/time-picker-popover.svelte +89 -0
  189. package/dist/timepicker/popover/time-picker-popover.svelte.d.ts +7 -0
  190. package/dist/timepicker/root/README.md +42 -0
  191. package/dist/timepicker/root/context.d.ts +51 -0
  192. package/dist/timepicker/root/context.js +15 -0
  193. package/dist/timepicker/root/time-picker-12h-test.svelte +22 -0
  194. package/dist/timepicker/root/time-picker-12h-test.svelte.d.ts +3 -0
  195. package/dist/timepicker/root/time-picker-bindable-test.svelte +25 -0
  196. package/dist/timepicker/root/time-picker-bindable-test.svelte.d.ts +3 -0
  197. package/dist/timepicker/root/time-picker-empty-test.svelte +20 -0
  198. package/dist/timepicker/root/time-picker-empty-test.svelte.d.ts +3 -0
  199. package/dist/timepicker/root/time-picker-root.svelte +625 -0
  200. package/dist/timepicker/root/time-picker-root.svelte.d.ts +28 -0
  201. package/dist/timepicker/root/time-picker-test.svelte +72 -0
  202. package/dist/timepicker/root/time-picker-test.svelte.d.ts +15 -0
  203. package/dist/timepicker/root/time-utils.d.ts +1 -0
  204. package/dist/timepicker/root/time-utils.js +3 -0
  205. package/dist/timepicker/segment/README.md +14 -0
  206. package/dist/timepicker/segment/time-picker-segment.svelte +365 -0
  207. package/dist/timepicker/segment/time-picker-segment.svelte.d.ts +9 -0
  208. package/dist/timepicker/trigger/README.md +14 -0
  209. package/dist/timepicker/trigger/time-picker-trigger-forwarding-test.svelte +35 -0
  210. package/dist/timepicker/trigger/time-picker-trigger-forwarding-test.svelte.d.ts +3 -0
  211. package/dist/timepicker/trigger/time-picker-trigger.svelte +122 -0
  212. package/dist/timepicker/trigger/time-picker-trigger.svelte.d.ts +9 -0
  213. package/dist/utils/date-only.d.ts +11 -0
  214. package/dist/utils/date-only.js +53 -0
  215. package/dist/utils/index.d.ts +1 -0
  216. package/dist/utils/index.js +1 -0
  217. package/package.json +16 -1
@@ -2,6 +2,7 @@ import type { CalendarSelectionMode, CalendarValue } from './context';
2
2
  type Props = {
3
3
  selectionMode?: CalendarSelectionMode;
4
4
  visibleMonths?: number;
5
+ showOutsideDays?: boolean;
5
6
  isDisabled?: boolean;
6
7
  isReadOnly?: boolean;
7
8
  defaultValue?: CalendarValue;
@@ -34,6 +34,7 @@
34
34
  type CalendarRootProps = {
35
35
  selectionMode?: CalendarSelectionMode;
36
36
  visibleMonths?: number;
37
+ showOutsideDays?: boolean;
37
38
  isDateUnavailable?: (date: string) => boolean;
38
39
  isDisabled?: boolean;
39
40
  isReadOnly?: boolean;
@@ -62,6 +63,7 @@
62
63
  let {
63
64
  selectionMode = 'single',
64
65
  visibleMonths = 1,
66
+ showOutsideDays = false,
65
67
  isDateUnavailable,
66
68
  isDisabled = false,
67
69
  isReadOnly = false,
@@ -100,6 +102,7 @@
100
102
  return {
101
103
  selectionMode,
102
104
  visibleMonths,
105
+ showOutsideDays,
103
106
  locale: resolvedLocale,
104
107
  isDateUnavailable,
105
108
  isDisabled,
@@ -4,6 +4,7 @@ import { type CalendarDateValue } from './date-utils';
4
4
  type CalendarRootProps = {
5
5
  selectionMode?: CalendarSelectionMode;
6
6
  visibleMonths?: number;
7
+ showOutsideDays?: boolean;
7
8
  isDateUnavailable?: (date: string) => boolean;
8
9
  isDisabled?: boolean;
9
10
  isReadOnly?: boolean;
@@ -16,6 +16,7 @@ export type CalendarMonth = {
16
16
  export type CreateCalendarContextOptions<TSelectionMode extends CalendarSelectionMode = CalendarSelectionMode> = {
17
17
  selectionMode?: TSelectionMode;
18
18
  visibleMonths?: number;
19
+ showOutsideDays?: boolean;
19
20
  locale?: string;
20
21
  isDisabled?: boolean;
21
22
  isReadOnly?: boolean;
@@ -31,12 +32,14 @@ export type CalendarContext = {
31
32
  selectionMode: CalendarSelectionMode;
32
33
  firstDayOfWeek: number;
33
34
  visibleMonths: number;
35
+ showOutsideDays: boolean;
34
36
  isDisabled: boolean;
35
37
  isReadOnly: boolean;
36
38
  months: CalendarMonth[];
37
39
  selectedValue: CalendarDateValue | undefined;
38
40
  rangeValue: CalendarRangeValue | undefined;
39
41
  focusedValue: CalendarDateValue;
42
+ focusVisible: boolean;
40
43
  weekdayLabels: string[];
41
44
  headingLabel: string;
42
45
  isSelected: (date: CalendarDateValue) => boolean;
@@ -47,6 +50,7 @@ export type CalendarContext = {
47
50
  isDateDisabled: (date: CalendarDateValue) => boolean;
48
51
  isOutsideVisibleRange: (date: CalendarDateValue, monthIndex: number) => boolean;
49
52
  setFocusedValue: (date: CalendarDateValue) => void;
53
+ setFocusVisible: (visible: boolean) => void;
50
54
  setHoveredValue: (date: CalendarDateValue | undefined) => void;
51
55
  selectDate: (date: CalendarDateValue) => void;
52
56
  goToNextPage: () => void;
@@ -2,9 +2,8 @@ import { getContext, setContext } from 'svelte';
2
2
  import { writable } from 'svelte/store';
3
3
  import { addDays, addMonths, buildMonthGrid, compareDates, formatCalendarDate, formatMonthHeading, getFirstDayOfWeek, getTodayUtcDate, getWeekdayLabels, isValidCalendarDateValue, parseCalendarDate, startOfMonth } from './date-utils';
4
4
  const KEY = Symbol('calendar');
5
- const MAX_FOCUS_SEARCH_DAYS = 370;
6
5
  export function createCalendarContext(options) {
7
- let { selectionMode = 'single', visibleMonths = 1, locale = Intl.DateTimeFormat().resolvedOptions().locale, isDisabled = false, isReadOnly = false, isDateUnavailable, onChange } = options;
6
+ let { selectionMode = 'single', visibleMonths = 1, showOutsideDays = false, locale = Intl.DateTimeFormat().resolvedOptions().locale, isDisabled = false, isReadOnly = false, isDateUnavailable, onChange } = options;
8
7
  const { value, defaultValue } = options;
9
8
  function isRangeValue(valueToCheck) {
10
9
  if (!valueToCheck || typeof valueToCheck === 'string')
@@ -85,6 +84,7 @@ export function createCalendarContext(options) {
85
84
  initialRangeSelected?.end ??
86
85
  initialRangeSelected?.start ??
87
86
  fallbackToday;
87
+ let currentFocusVisible = false;
88
88
  let currentVisibleMonth = startOfMonth(parseCalendarDate(currentFocused) ?? getTodayUtcDate());
89
89
  let cachedMonths = [];
90
90
  let hasCachedMonths = false;
@@ -93,6 +93,7 @@ export function createCalendarContext(options) {
93
93
  let pendingRangePathCacheStart;
94
94
  let previousUnavailableFn = isDateUnavailable;
95
95
  let previousVisibleMonths = visibleMonths;
96
+ let previousShowOutsideDays = showOutsideDays;
96
97
  let previousLocale = locale;
97
98
  let cachedFirstDayOfWeek = getFirstDayOfWeek(locale);
98
99
  const layoutVersion = writable(0);
@@ -133,16 +134,19 @@ export function createCalendarContext(options) {
133
134
  let shouldNotifySelection = false;
134
135
  const nextSelectionMode = next.selectionMode ?? 'single';
135
136
  const nextVisibleMonths = Math.max(1, next.visibleMonths ?? 1);
137
+ const nextShowOutsideDays = next.showOutsideDays ?? false;
136
138
  const nextLocale = next.locale ?? Intl.DateTimeFormat().resolvedOptions().locale;
137
139
  const nextUnavailableFn = next.isDateUnavailable;
138
140
  if (nextUnavailableFn !== previousUnavailableFn ||
139
141
  nextVisibleMonths !== previousVisibleMonths ||
142
+ nextShowOutsideDays !== previousShowOutsideDays ||
140
143
  nextLocale !== previousLocale) {
141
144
  clearUnavailableCache();
142
145
  shouldNotifyLayout = true;
143
146
  }
144
147
  previousUnavailableFn = nextUnavailableFn;
145
148
  previousVisibleMonths = nextVisibleMonths;
149
+ previousShowOutsideDays = nextShowOutsideDays;
146
150
  previousLocale = nextLocale;
147
151
  if (selectionMode !== nextSelectionMode) {
148
152
  selectionMode = nextSelectionMode;
@@ -157,6 +161,7 @@ export function createCalendarContext(options) {
157
161
  shouldNotifyLayout = true;
158
162
  }
159
163
  visibleMonths = nextVisibleMonths;
164
+ showOutsideDays = nextShowOutsideDays;
160
165
  locale = nextLocale;
161
166
  cachedFirstDayOfWeek = getFirstDayOfWeek(locale);
162
167
  if (isDisabled !== (next.isDisabled ?? false) || isReadOnly !== (next.isReadOnly ?? false)) {
@@ -312,7 +317,7 @@ export function createCalendarContext(options) {
312
317
  monthIndex,
313
318
  monthStart,
314
319
  heading: formatMonthHeading(monthStart, locale),
315
- weeks: buildMonthGrid(monthStart, firstDayOfWeek)
320
+ weeks: buildMonthGrid(monthStart, firstDayOfWeek, showOutsideDays)
316
321
  };
317
322
  });
318
323
  hasCachedMonths = true;
@@ -359,7 +364,7 @@ export function createCalendarContext(options) {
359
364
  return result;
360
365
  }
361
366
  function isDateDisabled(date) {
362
- if (isDisabled || isUnavailable(date))
367
+ if (isDisabled)
363
368
  return true;
364
369
  if (selectionMode === 'range' && currentRangeStart && !currentRangeEnd) {
365
370
  return !isPendingRangePathSelectable(date);
@@ -386,6 +391,12 @@ export function createCalendarContext(options) {
386
391
  }
387
392
  notifySelection();
388
393
  }
394
+ function setFocusVisible(visible) {
395
+ if (currentFocusVisible === visible)
396
+ return;
397
+ currentFocusVisible = visible;
398
+ notifySelection();
399
+ }
389
400
  function setHoveredValue(date) {
390
401
  if (selectionMode !== 'range')
391
402
  return;
@@ -514,7 +525,7 @@ export function createCalendarContext(options) {
514
525
  return undefined;
515
526
  const next = addDays(parsed, amount);
516
527
  const nextValue = formatCalendarDate(next);
517
- const focusableValue = findFocusableDate(nextValue, amount);
528
+ const focusableValue = findFocusableDate(nextValue);
518
529
  if (!focusableValue)
519
530
  return undefined;
520
531
  setFocusedValue(focusableValue);
@@ -526,8 +537,7 @@ export function createCalendarContext(options) {
526
537
  return undefined;
527
538
  const next = addMonths(parsed, amount);
528
539
  const nextValue = formatCalendarDate(next);
529
- const dayStep = amount >= 0 ? 1 : -1;
530
- const focusableValue = findFocusableDate(nextValue, dayStep);
540
+ const focusableValue = findFocusableDate(nextValue);
531
541
  if (!focusableValue) {
532
542
  if (selectionMode === 'range' && currentRangeStart && !currentRangeEnd) {
533
543
  return moveToMonthEdge(baseDate, amount >= 0 ? 'end' : 'start');
@@ -545,33 +555,19 @@ export function createCalendarContext(options) {
545
555
  const monthEnd = addDays(addMonths(monthStart, 1), -1);
546
556
  const targetDate = edge === 'start' ? monthStart : monthEnd;
547
557
  const nextValue = formatCalendarDate(targetDate);
548
- const dayStep = edge === 'start' ? 1 : -1;
549
- const focusableValue = findFocusableDate(nextValue, dayStep);
558
+ const focusableValue = findFocusableDate(nextValue);
550
559
  if (!focusableValue)
551
560
  return undefined;
552
561
  setFocusedValue(focusableValue);
553
562
  return focusableValue;
554
563
  }
555
- function findFocusableDate(targetDate, dayStep) {
564
+ function findFocusableDate(targetDate) {
556
565
  if (isDisabled)
557
566
  return undefined;
558
- if (!isDateDisabled(targetDate))
559
- return targetDate;
560
- if (dayStep === 0)
561
- return undefined;
562
- let current = parseCalendarDate(targetDate);
563
- if (!current)
564
- return undefined;
565
- for (let index = 0; index < MAX_FOCUS_SEARCH_DAYS; index++) {
566
- current = addDays(current, dayStep > 0 ? 1 : -1);
567
- const candidate = formatCalendarDate(current);
568
- if (!isDateDisabled(candidate)) {
569
- return candidate;
570
- }
571
- }
572
- return undefined;
567
+ return targetDate;
573
568
  }
574
569
  function handleCellKeydown(event, date) {
570
+ setFocusVisible(true);
575
571
  const keyDate = isValidCalendarDateValue(currentFocused) ? currentFocused : date;
576
572
  let movedDate;
577
573
  function extendRangeWithKeyboard(nextDate) {
@@ -668,6 +664,9 @@ export function createCalendarContext(options) {
668
664
  get visibleMonths() {
669
665
  return visibleMonths;
670
666
  },
667
+ get showOutsideDays() {
668
+ return showOutsideDays;
669
+ },
671
670
  get isDisabled() {
672
671
  return isDisabled;
673
672
  },
@@ -686,6 +685,9 @@ export function createCalendarContext(options) {
686
685
  get focusedValue() {
687
686
  return currentFocused;
688
687
  },
688
+ get focusVisible() {
689
+ return currentFocusVisible;
690
+ },
689
691
  get weekdayLabels() {
690
692
  return getWeekdayLabels(locale, cachedFirstDayOfWeek);
691
693
  },
@@ -700,6 +702,7 @@ export function createCalendarContext(options) {
700
702
  isDateDisabled,
701
703
  isOutsideVisibleRange,
702
704
  setFocusedValue,
705
+ setFocusVisible,
703
706
  setHoveredValue,
704
707
  selectDate,
705
708
  goToNextPage,
@@ -13,5 +13,5 @@ export type CalendarDayCell = {
13
13
  date: CalendarDateValue;
14
14
  isOutsideMonth: boolean;
15
15
  };
16
- export declare function buildMonthGrid(monthStart: Date, firstDayOfWeek: number): CalendarDayCell[][];
16
+ export declare function buildMonthGrid(monthStart: Date, firstDayOfWeek: number, showOutsideDays?: boolean): CalendarDayCell[][];
17
17
  export declare function formatMonthHeading(date: Date, locale: string): string;
@@ -1,43 +1,28 @@
1
- const DATE_RE = /^(\d{4})-(\d{2})-(\d{2})$/;
1
+ import { createUtcDate, formatDateOnlyValue, isValidDateOnlyValue, parseDateOnlyValue } from '../../utils/date-only';
2
2
  export function isValidCalendarDateValue(value) {
3
- const match = DATE_RE.exec(value);
4
- if (!match)
5
- return false;
6
- const year = Number(match[1]);
7
- const month = Number(match[2]);
8
- const day = Number(match[3]);
9
- if (month < 1 || month > 12 || day < 1 || day > 31)
10
- return false;
11
- const date = new Date(Date.UTC(year, month - 1, day));
12
- return (date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day);
3
+ return isValidDateOnlyValue(value);
13
4
  }
14
5
  export function parseCalendarDate(value) {
15
- if (!isValidCalendarDateValue(value))
16
- return null;
17
- const [year, month, day] = value.split('-').map(Number);
18
- return new Date(Date.UTC(year, month - 1, day));
6
+ return parseDateOnlyValue(value);
19
7
  }
20
8
  export function formatCalendarDate(date) {
21
- const year = date.getUTCFullYear();
22
- const month = String(date.getUTCMonth() + 1).padStart(2, '0');
23
- const day = String(date.getUTCDate()).padStart(2, '0');
24
- return `${year}-${month}-${day}`;
9
+ return formatDateOnlyValue(date);
25
10
  }
26
11
  export function startOfMonth(date) {
27
- return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1));
12
+ return createUtcDate(date.getUTCFullYear(), date.getUTCMonth(), 1);
28
13
  }
29
14
  function getDaysInMonthUtc(year, monthIndex) {
30
- return new Date(Date.UTC(year, monthIndex + 1, 0)).getUTCDate();
15
+ return createUtcDate(year, monthIndex + 1, 0).getUTCDate();
31
16
  }
32
17
  export function addMonths(date, amount) {
33
18
  const targetMonth = date.getUTCMonth() + amount;
34
19
  const targetYear = date.getUTCFullYear() + Math.floor(targetMonth / 12);
35
20
  const normalizedMonth = ((targetMonth % 12) + 12) % 12;
36
21
  const targetDay = Math.min(date.getUTCDate(), getDaysInMonthUtc(targetYear, normalizedMonth));
37
- return new Date(Date.UTC(targetYear, normalizedMonth, targetDay));
22
+ return createUtcDate(targetYear, normalizedMonth, targetDay);
38
23
  }
39
24
  export function addDays(date, amount) {
40
- return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate() + amount));
25
+ return createUtcDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate() + amount);
41
26
  }
42
27
  export function compareDates(a, b) {
43
28
  const at = a.getTime();
@@ -50,7 +35,7 @@ export function compareDates(a, b) {
50
35
  }
51
36
  export function getTodayUtcDate() {
52
37
  const now = new Date();
53
- return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
38
+ return createUtcDate(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());
54
39
  }
55
40
  export function getFirstDayOfWeek(locale) {
56
41
  try {
@@ -76,13 +61,18 @@ export function getWeekdayLabels(locale, firstDayOfWeek) {
76
61
  return formatter.format(addDays(sunday, dayOffset));
77
62
  });
78
63
  }
79
- export function buildMonthGrid(monthStart, firstDayOfWeek) {
64
+ export function buildMonthGrid(monthStart, firstDayOfWeek, showOutsideDays = true) {
80
65
  const firstOfMonth = startOfMonth(monthStart);
81
66
  const firstWeekday = firstOfMonth.getUTCDay();
82
67
  const startOffset = (firstWeekday - firstDayOfWeek + 7) % 7;
83
68
  const gridStart = addDays(firstOfMonth, -startOffset);
69
+ const lastOfMonth = addDays(addMonths(firstOfMonth, 1), -1);
70
+ const lastWeekday = lastOfMonth.getUTCDay();
71
+ const endOffset = (firstDayOfWeek + 6 - lastWeekday + 7) % 7;
72
+ const visibleDays = startOffset + lastOfMonth.getUTCDate() + endOffset;
73
+ const weekCount = showOutsideDays ? 6 : Math.max(1, Math.ceil(visibleDays / 7));
84
74
  const weeks = [];
85
- for (let weekIndex = 0; weekIndex < 6; weekIndex++) {
75
+ for (let weekIndex = 0; weekIndex < weekCount; weekIndex++) {
86
76
  const week = [];
87
77
  for (let dayIndex = 0; dayIndex < 7; dayIndex++) {
88
78
  const date = addDays(gridStart, weekIndex * 7 + dayIndex);
@@ -0,0 +1,14 @@
1
+ # Calendar TriggerNext
2
+
3
+ ## API reference
4
+
5
+ ### Calendar.TriggerNext
6
+
7
+ Name: `Calendar.TriggerNext`
8
+ Description: Button part that advances the visible calendar period.
9
+
10
+ | Prop | Type | Default | Description |
11
+ | -------------- | ---------------------- | ----------- | ------------------------------------------------------ |
12
+ | `children` | `Snippet` | `undefined` | Optional trigger content. |
13
+ | `class` | `string` | `''` | CSS class names for the trigger button. |
14
+ | `...restProps` | `HTMLButtonAttributes` | `-` | Additional button attributes forwarded to the trigger. |
@@ -0,0 +1,14 @@
1
+ # Calendar TriggerPrevious
2
+
3
+ ## API reference
4
+
5
+ ### Calendar.TriggerPrevious
6
+
7
+ Name: `Calendar.TriggerPrevious`
8
+ Description: Button part that moves the visible calendar period backward.
9
+
10
+ | Prop | Type | Default | Description |
11
+ | -------------- | ---------------------- | ----------- | ------------------------------------------------------ |
12
+ | `children` | `Snippet` | `undefined` | Optional trigger content. |
13
+ | `class` | `string` | `''` | CSS class names for the trigger button. |
14
+ | `...restProps` | `HTMLButtonAttributes` | `-` | Additional button attributes forwarded to the trigger. |
@@ -0,0 +1,75 @@
1
+ # Clock
2
+
3
+ ## Description
4
+
5
+ `Clock` provides a standalone wheel-based time picker with spinbutton columns for hour, minute, second, and day-period selection. It can be used independently or composed inside `TimePicker` via `TimePicker.Clock`.
6
+
7
+ ## Anatomy
8
+
9
+ - `Clock.Root`
10
+ - `Clock.Axis`
11
+ - `Clock.WheelColumn`
12
+ - `Clock.WheelItem`
13
+
14
+ ```svelte
15
+ <Clock.Root value="14:30" granularity="minute" hourCycle={24} class="flex gap-2">
16
+ {#snippet column(col)}
17
+ <Clock.WheelColumn type={col.type} class="h-44 w-16">
18
+ {#snippet children(option)}
19
+ <Clock.WheelItem type={col.type} {option} class="..." />
20
+ {/snippet}
21
+ </Clock.WheelColumn>
22
+ {/snippet}
23
+ <Clock.Axis class="rounded-md ring-1 ring-inset" />
24
+ </Clock.Root>
25
+ ```
26
+
27
+ Default columns are rendered automatically when no `column` snippet is provided:
28
+
29
+ ```svelte
30
+ <Clock.Root value="09:00" granularity="minute" hourCycle={24} />
31
+ ```
32
+
33
+ ## Root API
34
+
35
+ - `value?: string | null` (`HH:mm` or `HH:mm:ss`)
36
+ - `defaultValue?: string | null` (`HH:mm` or `HH:mm:ss`)
37
+ - `onChange?: (value: string | null) => void`
38
+ - `minValue?: string`
39
+ - `maxValue?: string`
40
+ - `hourCycle?: 12 | 24` (defaults to locale)
41
+ - `granularity?: 'hour' | 'minute' | 'second'` (defaults to `'minute'`)
42
+ - `hourStep?: number`
43
+ - `minuteStep?: number`
44
+ - `secondStep?: number`
45
+ - `isDisabled?: boolean`
46
+ - `column?: Snippet<[ClockColumnInfo]>` — custom per-column rendering
47
+ - `children?: Snippet` — arbitrary children. When `column` is used, children render after columns (useful for overlays like `Clock.Axis`).
48
+ - `class?: string`
49
+ - `aria-label?: string`
50
+
51
+ Visible columns are resolved automatically in stable order: `hour → minute? → second? → dayPeriod?`.
52
+
53
+ ## Wheel API
54
+
55
+ - `Clock.Axis` renders a root-level visual overlay (for example, a central selection band) across all columns.
56
+ - `Clock.WheelColumn` renders one wheel (`role="spinbutton"`) for one editable segment (`hour`, `minute`, `second`, or `dayPeriod`).
57
+ - `Clock.WheelItem` is headless: it renders one item (`data-wheel-item`) with state attributes (`data-selected`, `data-disabled`, `data-centered`) and leaves all visual styling to consumers.
58
+ - `ClockColumnInfo` shape:
59
+ - `type: 'hour' | 'minute' | 'second' | 'dayPeriod'`
60
+ - `label?: string`
61
+
62
+ ## Accessibility
63
+
64
+ - Each wheel column exposes `role="spinbutton"` with `aria-valuenow`, `aria-valuetext`, `aria-valuemin`, and `aria-valuemax`.
65
+ - `ArrowUp/ArrowDown`: change value by one step.
66
+ - `ArrowLeft/ArrowRight`: move focus between columns.
67
+ - `Home/End`: jump to first/last value in the column.
68
+
69
+ ## Notes
70
+
71
+ - Locale is read from `LocaleProvider` when available.
72
+ - Internally, values are normalized to 24-hour representation; 12-hour rendering only affects UI segments.
73
+ - `granularity='hour'` emits `HH:00` values.
74
+ - Min/max comparisons do not support midnight-wrapping ranges.
75
+ - Wheel selection commits immediately on snap.
@@ -0,0 +1,24 @@
1
+ # Clock Axis
2
+
3
+ ## API reference
4
+
5
+ ### Clock.Axis
6
+
7
+ Name: `Clock.Axis`
8
+ Description: Visual overlay band for `Clock.Root`; it is presentational (`aria-hidden`) and does not manage selection.
9
+
10
+ | Prop | Type | Default | Description |
11
+ | -------------- | -------------------------------- | ----------- | ---------------------------------------------------- |
12
+ | `height` | `number` | `undefined` | Optional overlay height in pixels. |
13
+ | `class` | `string` | `''` | CSS class names for the overlay element. |
14
+ | `style` | `string` | `''` | Inline styles merged with the optional height style. |
15
+ | `...restProps` | `HTMLAttributes<HTMLDivElement>` | `-` | Additional attributes forwarded to the overlay div. |
16
+
17
+ ### Context utilities
18
+
19
+ Name: `useClockContext`
20
+ Description: Ensures `Clock.Axis` is used within `Clock.Root`.
21
+
22
+ | Prop | Type | Default | Description |
23
+ | ----------------- | -------------------- | ------- | ------------------------------------------------ |
24
+ | `useClockContext` | `() => ClockContext` | `-` | Returns context and throws outside `Clock.Root`. |
@@ -0,0 +1,37 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ import { useClockContext } from '../root/context';
4
+
5
+ type ClockAxisProps = Omit<HTMLAttributes<HTMLDivElement>, 'class' | 'children'> & {
6
+ class?: string;
7
+ height?: number;
8
+ };
9
+
10
+ let {
11
+ class: className = '',
12
+ height,
13
+ style: styleProp = '',
14
+ ...restProps
15
+ }: ClockAxisProps = $props();
16
+
17
+ const resolvedStyle = $derived.by(() => {
18
+ const base = styleProp?.trim() ?? '';
19
+ if (height === undefined || !Number.isFinite(height) || height <= 0) {
20
+ return base.length > 0 ? base : undefined;
21
+ }
22
+ const axisHeight = `height:${height}px`;
23
+ if (base.length === 0) return axisHeight;
24
+ const withSemicolon = /;\s*$/.test(base) ? base : `${base};`;
25
+ return `${withSemicolon}${axisHeight}`;
26
+ });
27
+
28
+ useClockContext();
29
+ </script>
30
+
31
+ <div
32
+ aria-hidden="true"
33
+ data-clock-axis
34
+ class={`pointer-events-none absolute top-1/2 left-0 w-full -translate-y-1/2 ${className}`}
35
+ style={resolvedStyle}
36
+ {...restProps}
37
+ ></div>
@@ -0,0 +1,8 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ type ClockAxisProps = Omit<HTMLAttributes<HTMLDivElement>, 'class' | 'children'> & {
3
+ class?: string;
4
+ height?: number;
5
+ };
6
+ declare const ClockAxis: import("svelte").Component<ClockAxisProps, {}, "">;
7
+ type ClockAxis = ReturnType<typeof ClockAxis>;
8
+ export default ClockAxis;
@@ -0,0 +1,16 @@
1
+ export type WheelScrollBehavior = 'smooth' | 'instant';
2
+ export type WheelScrollApi = {
3
+ scrollToIndex: (index: number, behavior?: WheelScrollBehavior, options?: {
4
+ silent?: boolean;
5
+ }) => void;
6
+ destroy: () => void;
7
+ };
8
+ /**
9
+ * Manages scroll-based item selection for a wheel column.
10
+ *
11
+ * All snapping is handled in JavaScript (no CSS `scroll-snap-type`).
12
+ * After scrolling settles (either short inactivity in `scroll` events
13
+ * or browser `scrollend`), we find the nearest centered item and run
14
+ * a fast 120 ms ease-out animation to align it.
15
+ */
16
+ export declare function useWheelScroll(container: HTMLElement, onSnap: (centeredIndex: number) => number | null | void): WheelScrollApi;