@warkypublic/svelix 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/LICENSE +73 -0
  2. package/README.md +3 -0
  3. package/dist/actions/index.d.ts +1 -0
  4. package/dist/actions/index.js +2 -0
  5. package/dist/components/BetterMenu/BetterMenu.stories.js +68 -0
  6. package/dist/components/BetterMenu/BetterMenu.svelte +38 -0
  7. package/dist/components/BetterMenu/BetterMenu.svelte.d.ts +14 -0
  8. package/dist/components/BetterMenu/BetterMenuAsyncButton.svelte +34 -0
  9. package/dist/components/BetterMenu/BetterMenuAsyncButton.svelte.d.ts +8 -0
  10. package/dist/components/BetterMenu/BetterMenuPreview.svelte +43 -0
  11. package/dist/components/BetterMenu/BetterMenuPreview.svelte.d.ts +7 -0
  12. package/dist/components/BetterMenu/MenuRenderer.svelte +75 -0
  13. package/dist/components/BetterMenu/MenuRenderer.svelte.d.ts +6 -0
  14. package/dist/components/BetterMenu/Plan.mdx +155 -0
  15. package/dist/components/BetterMenu/index.d.ts +4 -0
  16. package/dist/components/BetterMenu/index.js +4 -0
  17. package/dist/components/BetterMenu/store.d.ts +10 -0
  18. package/dist/components/BetterMenu/store.js +48 -0
  19. package/dist/components/BetterMenu/types.d.ts +24 -0
  20. package/dist/components/BetterMenu/types.js +1 -0
  21. package/dist/components/Boxer/Boxer.stories.d.ts +19 -0
  22. package/dist/components/Boxer/Boxer.stories.js +102 -0
  23. package/dist/components/Boxer/Boxer.svelte +411 -0
  24. package/dist/components/Boxer/Boxer.svelte.d.ts +11 -0
  25. package/dist/components/Boxer/BoxerTarget.svelte +88 -0
  26. package/dist/components/Boxer/BoxerTarget.svelte.d.ts +20 -0
  27. package/dist/components/Boxer/Plan.mdx +140 -0
  28. package/dist/components/Boxer/features.mdx +81 -0
  29. package/dist/components/Boxer/index.d.ts +4 -0
  30. package/dist/components/Boxer/index.js +4 -0
  31. package/dist/components/Boxer/store.d.ts +26 -0
  32. package/dist/components/Boxer/store.js +103 -0
  33. package/dist/components/Boxer/types.d.ts +46 -0
  34. package/dist/components/Boxer/types.js +1 -0
  35. package/dist/components/Button.stories.d.ts +11 -0
  36. package/dist/components/Button.stories.js +109 -0
  37. package/dist/components/Button.svelte +50 -0
  38. package/dist/components/Button.svelte.d.ts +12 -0
  39. package/dist/components/ButtonPreview.svelte +14 -0
  40. package/dist/components/ButtonPreview.svelte.d.ts +4 -0
  41. package/dist/components/ErrorBoundary/ErrorBoundary.stories.js +17 -0
  42. package/dist/components/ErrorBoundary/ErrorBoundary.svelte +127 -0
  43. package/dist/components/ErrorBoundary/ErrorBoundary.svelte.d.ts +13 -0
  44. package/dist/components/ErrorBoundary/ErrorBoundaryPreview.svelte +28 -0
  45. package/dist/components/ErrorBoundary/ErrorBoundaryPreview.svelte.d.ts +6 -0
  46. package/dist/components/ErrorBoundary/ErrorManager.d.ts +15 -0
  47. package/dist/components/ErrorBoundary/ErrorManager.js +158 -0
  48. package/dist/components/ErrorBoundary/Plan.mdx +182 -0
  49. package/dist/components/ErrorBoundary/index.d.ts +3 -0
  50. package/dist/components/ErrorBoundary/index.js +3 -0
  51. package/dist/components/ErrorBoundary/types.d.ts +43 -0
  52. package/dist/components/ErrorBoundary/types.js +1 -0
  53. package/dist/components/Former/Former.stories.js +228 -0
  54. package/dist/components/Former/Former.svelte +405 -0
  55. package/dist/components/Former/Former.svelte.d.ts +33 -0
  56. package/dist/components/Former/FormerButtonArea.svelte +93 -0
  57. package/dist/components/Former/FormerButtonArea.svelte.d.ts +15 -0
  58. package/dist/components/Former/FormerDrawer.svelte +115 -0
  59. package/dist/components/Former/FormerDrawer.svelte.d.ts +19 -0
  60. package/dist/components/Former/FormerDrawerPreview.svelte +226 -0
  61. package/dist/components/Former/FormerDrawerPreview.svelte.d.ts +7 -0
  62. package/dist/components/Former/FormerModal.svelte +108 -0
  63. package/dist/components/Former/FormerModal.svelte.d.ts +14 -0
  64. package/dist/components/Former/FormerModalPreview.svelte +226 -0
  65. package/dist/components/Former/FormerModalPreview.svelte.d.ts +7 -0
  66. package/dist/components/Former/FormerPreview.svelte +238 -0
  67. package/dist/components/Former/FormerPreview.svelte.d.ts +8 -0
  68. package/dist/components/Former/FormerResolveSpecAPI.d.ts +26 -0
  69. package/dist/components/Former/FormerResolveSpecAPI.js +44 -0
  70. package/dist/components/Former/FormerRestApiPreview.svelte +198 -0
  71. package/dist/components/Former/FormerRestApiPreview.svelte.d.ts +3 -0
  72. package/dist/components/Former/FormerRestHeadSpecAPI.d.ts +8 -0
  73. package/dist/components/Former/FormerRestHeadSpecAPI.js +38 -0
  74. package/dist/components/Former/Plan.mdx +115 -0
  75. package/dist/components/Former/formerState.svelte.d.ts +21 -0
  76. package/dist/components/Former/formerState.svelte.js +57 -0
  77. package/dist/components/Former/index.d.ts +8 -0
  78. package/dist/components/Former/index.js +8 -0
  79. package/dist/components/Former/types.d.ts +61 -0
  80. package/dist/components/Former/types.js +1 -0
  81. package/dist/components/FormerControllers/ButtonCtrl.stories.js +102 -0
  82. package/dist/components/FormerControllers/ButtonCtrl.svelte +65 -0
  83. package/dist/components/FormerControllers/ButtonCtrl.svelte.d.ts +14 -0
  84. package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrl.stories.js +73 -0
  85. package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrl.svelte +630 -0
  86. package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrl.svelte.d.ts +54 -0
  87. package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrl.utils.d.ts +40 -0
  88. package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrl.utils.js +688 -0
  89. package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlCalendar.svelte +193 -0
  90. package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlCalendar.svelte.d.ts +13 -0
  91. package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlPickerPanel.svelte +119 -0
  92. package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlPickerPanel.svelte.d.ts +39 -0
  93. package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlTimeFields.svelte +343 -0
  94. package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlTimeFields.svelte.d.ts +27 -0
  95. package/dist/components/FormerControllers/DateTimeCtrl/index.d.ts +2 -0
  96. package/dist/components/FormerControllers/DateTimeCtrl/index.js +1 -0
  97. package/dist/components/FormerControllers/FormerControllers.stories.js +76 -0
  98. package/dist/components/FormerControllers/IconButtonCtrl.stories.js +77 -0
  99. package/dist/components/FormerControllers/IconButtonCtrl.svelte +64 -0
  100. package/dist/components/FormerControllers/IconButtonCtrl.svelte.d.ts +13 -0
  101. package/dist/components/FormerControllers/InlineWrapper.stories.js +133 -0
  102. package/dist/components/FormerControllers/InlineWrapper.svelte +85 -0
  103. package/dist/components/FormerControllers/InlineWrapper.svelte.d.ts +16 -0
  104. package/dist/components/FormerControllers/InlineWrapperPreview.svelte +41 -0
  105. package/dist/components/FormerControllers/InlineWrapperPreview.svelte.d.ts +13 -0
  106. package/dist/components/FormerControllers/NativeSelectCtrl.stories.js +79 -0
  107. package/dist/components/FormerControllers/NativeSelectCtrl.svelte +61 -0
  108. package/dist/components/FormerControllers/NativeSelectCtrl.svelte.d.ts +13 -0
  109. package/dist/components/FormerControllers/NumberInputCtrl.stories.js +77 -0
  110. package/dist/components/FormerControllers/NumberInputCtrl.svelte +61 -0
  111. package/dist/components/FormerControllers/NumberInputCtrl.svelte.d.ts +11 -0
  112. package/dist/components/FormerControllers/PasswordInputCtrl.stories.js +79 -0
  113. package/dist/components/FormerControllers/PasswordInputCtrl.svelte +57 -0
  114. package/dist/components/FormerControllers/PasswordInputCtrl.svelte.d.ts +8 -0
  115. package/dist/components/FormerControllers/Plan.mdx +129 -0
  116. package/dist/components/FormerControllers/SwitchCtrl.stories.js +73 -0
  117. package/dist/components/FormerControllers/SwitchCtrl.svelte +38 -0
  118. package/dist/components/FormerControllers/SwitchCtrl.svelte.d.ts +8 -0
  119. package/dist/components/FormerControllers/TextAreaCtrl.stories.js +71 -0
  120. package/dist/components/FormerControllers/TextAreaCtrl.svelte +47 -0
  121. package/dist/components/FormerControllers/TextAreaCtrl.svelte.d.ts +9 -0
  122. package/dist/components/FormerControllers/TextInputCtrl.svelte +47 -0
  123. package/dist/components/FormerControllers/TextInputCtrl.svelte.d.ts +9 -0
  124. package/dist/components/FormerControllers/index.d.ts +12 -0
  125. package/dist/components/FormerControllers/index.js +11 -0
  126. package/dist/components/FormerControllers/types.d.ts +10 -0
  127. package/dist/components/FormerControllers/types.js +1 -0
  128. package/dist/components/GlobalStateStore/GlobalStateStore.d.ts +19 -0
  129. package/dist/components/GlobalStateStore/GlobalStateStore.js +349 -0
  130. package/dist/components/GlobalStateStore/GlobalStateStore.types.d.ts +127 -0
  131. package/dist/components/GlobalStateStore/GlobalStateStore.types.js +2 -0
  132. package/dist/components/GlobalStateStore/GlobalStateStore.utils.d.ts +4 -0
  133. package/dist/components/GlobalStateStore/GlobalStateStore.utils.js +92 -0
  134. package/dist/components/GlobalStateStore/GlobalStateStoreContext.d.ts +10 -0
  135. package/dist/components/GlobalStateStore/GlobalStateStoreContext.js +10 -0
  136. package/dist/components/GlobalStateStore/GlobalStateStoreProvider.svelte +113 -0
  137. package/dist/components/GlobalStateStore/GlobalStateStoreProvider.svelte.d.ts +16 -0
  138. package/dist/components/GlobalStateStore/index.d.ts +5 -0
  139. package/dist/components/GlobalStateStore/index.js +3 -0
  140. package/dist/components/Gridler/CellEditor.svelte +126 -0
  141. package/dist/components/Gridler/CellEditor.svelte.d.ts +15 -0
  142. package/dist/components/Gridler/Gridler.stories.d.ts +56 -0
  143. package/dist/components/Gridler/Gridler.stories.js +262 -0
  144. package/dist/components/Gridler/Gridler.svelte +778 -0
  145. package/dist/components/Gridler/Gridler.svelte.d.ts +11 -0
  146. package/dist/components/Gridler/GridlerHeader.svelte +179 -0
  147. package/dist/components/Gridler/GridlerHeader.svelte.d.ts +13 -0
  148. package/dist/components/Gridler/Plan.mdx +692 -0
  149. package/dist/components/Gridler/index.d.ts +6 -0
  150. package/dist/components/Gridler/index.js +6 -0
  151. package/dist/components/Gridler/types.d.ts +84 -0
  152. package/dist/components/Gridler/types.js +16 -0
  153. package/dist/components/Gridler/utils/rendering.d.ts +16 -0
  154. package/dist/components/Gridler/utils/rendering.js +202 -0
  155. package/dist/components/Gridler/utils/scrolling.d.ts +12 -0
  156. package/dist/components/Gridler/utils/scrolling.js +97 -0
  157. package/dist/components/Portal/Portal.mdx +125 -0
  158. package/dist/components/Portal/Portal.svelte +47 -0
  159. package/dist/components/Portal/Portal.svelte.d.ts +18 -0
  160. package/dist/components/Screenshot/Screenshot.stories.d.ts +16 -0
  161. package/dist/components/Screenshot/Screenshot.stories.js +15 -0
  162. package/dist/components/Screenshot/Screenshot.svelte +54 -0
  163. package/dist/components/Screenshot/Screenshot.svelte.d.ts +3 -0
  164. package/dist/components/Screenshot/Screenshot.util.d.ts +1 -0
  165. package/dist/components/Screenshot/Screenshot.util.js +49 -0
  166. package/dist/components/Screenshot/index.d.ts +2 -0
  167. package/dist/components/Screenshot/index.js +2 -0
  168. package/dist/components/Svark/Svark.stories.js +659 -0
  169. package/dist/components/Svark/Svark.svelte +691 -0
  170. package/dist/components/Svark/Svark.svelte.d.ts +26 -0
  171. package/dist/components/Svark/SvarkResolveSpecAdapter.d.ts +16 -0
  172. package/dist/components/Svark/SvarkResolveSpecAdapter.js +68 -0
  173. package/dist/components/Svark/SvarkSelectionDemo.svelte +59 -0
  174. package/dist/components/Svark/SvarkSelectionDemo.svelte.d.ts +4 -0
  175. package/dist/components/Svark/index.d.ts +3 -0
  176. package/dist/components/Svark/index.js +3 -0
  177. package/dist/components/Svark/types.d.ts +63 -0
  178. package/dist/components/Svark/types.js +1 -0
  179. package/dist/components/VTree/VTree.models.d.ts +12 -0
  180. package/dist/components/VTree/VTree.models.js +1 -0
  181. package/dist/components/VTree/VTree.stories.d.ts +40 -0
  182. package/dist/components/VTree/VTree.stories.js +112 -0
  183. package/dist/components/VTree/VTree.svelte +471 -0
  184. package/dist/components/VTree/VTree.svelte.d.ts +5 -0
  185. package/dist/components/VTree/VTreeContextMenu.svelte +40 -0
  186. package/dist/components/VTree/VTreeContextMenu.svelte.d.ts +11 -0
  187. package/dist/components/VTree/VTreeEventsDemo.svelte +88 -0
  188. package/dist/components/VTree/VTreeEventsDemo.svelte.d.ts +3 -0
  189. package/dist/components/VTree/VTreeResolveSpecAdapter.d.ts +14 -0
  190. package/dist/components/VTree/VTreeResolveSpecAdapter.js +103 -0
  191. package/dist/components/VTree/VTreeRow.svelte +136 -0
  192. package/dist/components/VTree/VTreeRow.svelte.d.ts +37 -0
  193. package/dist/components/VTree/VTreeSearch.svelte +25 -0
  194. package/dist/components/VTree/VTreeSearch.svelte.d.ts +8 -0
  195. package/dist/components/VTree/VTreeVirtualViewport.svelte +154 -0
  196. package/dist/components/VTree/VTreeVirtualViewport.svelte.d.ts +45 -0
  197. package/dist/components/VTree/index.d.ts +3 -0
  198. package/dist/components/VTree/index.js +3 -0
  199. package/dist/components/VTree/types.d.ts +83 -0
  200. package/dist/components/VTree/types.js +1 -0
  201. package/dist/components/index.d.ts +11 -0
  202. package/dist/components/index.js +11 -0
  203. package/dist/index.d.ts +16 -0
  204. package/dist/index.js +20 -0
  205. package/dist/stores/index.d.ts +1 -0
  206. package/dist/stores/index.js +1 -0
  207. package/dist/themes/svelix_orange.css +205 -0
  208. package/dist/utils/PropsWithChildren.d.ts +5 -0
  209. package/dist/utils/PropsWithChildren.js +1 -0
  210. package/dist/utils/index.d.ts +1 -0
  211. package/dist/utils/index.js +2 -0
  212. package/package.json +85 -0
@@ -0,0 +1,193 @@
1
+ <script lang="ts">
2
+ import { WEEKDAY_LABELS, type CalendarCell } from './DateTimeCtrl.utils';
3
+
4
+ interface Props {
5
+ cells: CalendarCell[];
6
+ isDateSelectable?: (isoDate: string) => boolean;
7
+ monthLabel: string;
8
+ onNext?: () => void;
9
+ onPrev?: () => void;
10
+ onSelect?: (isoDate: string) => void;
11
+ selectedIso?: string;
12
+ }
13
+
14
+ let {
15
+ cells,
16
+ isDateSelectable,
17
+ monthLabel,
18
+ onNext,
19
+ onPrev,
20
+ onSelect,
21
+ selectedIso,
22
+ }: Props = $props();
23
+
24
+ function isCellDisabled(cell: CalendarCell): boolean {
25
+ if (!cell.inCurrentMonth) {
26
+ return true;
27
+ }
28
+
29
+ if (!isDateSelectable) {
30
+ return false;
31
+ }
32
+
33
+ return !isDateSelectable(cell.isoDate);
34
+ }
35
+
36
+ function focusFirstAvailable(): void {
37
+ if (typeof document === 'undefined') {
38
+ return;
39
+ }
40
+
41
+ const first = cells.find((cell) => !isCellDisabled(cell));
42
+ if (!first) {
43
+ return;
44
+ }
45
+
46
+ const target = document.querySelector<HTMLButtonElement>(`button[data-iso="${first.isoDate}"]`);
47
+ target?.focus();
48
+ }
49
+
50
+ function focusFromIndex(startIndex: number, step: number): void {
51
+ if (typeof document === 'undefined') {
52
+ return;
53
+ }
54
+
55
+ let index = startIndex + step;
56
+ while (index >= 0 && index < cells.length) {
57
+ const nextCell = cells[index];
58
+ if (!isCellDisabled(nextCell)) {
59
+ const target = document.querySelector<HTMLButtonElement>(
60
+ `button[data-iso="${nextCell.isoDate}"]`,
61
+ );
62
+ target?.focus();
63
+ return;
64
+ }
65
+ index += step;
66
+ }
67
+ }
68
+
69
+ function handleDayKeydown(event: KeyboardEvent, index: number, cell: CalendarCell): void {
70
+ if (isCellDisabled(cell)) {
71
+ return;
72
+ }
73
+
74
+ if (event.key === 'Enter' || event.key === ' ') {
75
+ event.preventDefault();
76
+ onSelect?.(cell.isoDate);
77
+ return;
78
+ }
79
+
80
+ if (event.key === 'ArrowRight') {
81
+ event.preventDefault();
82
+ focusFromIndex(index, 1);
83
+ return;
84
+ }
85
+
86
+ if (event.key === 'ArrowLeft') {
87
+ event.preventDefault();
88
+ focusFromIndex(index, -1);
89
+ return;
90
+ }
91
+
92
+ if (event.key === 'ArrowDown') {
93
+ event.preventDefault();
94
+ focusFromIndex(index, 7);
95
+ return;
96
+ }
97
+
98
+ if (event.key === 'ArrowUp') {
99
+ event.preventDefault();
100
+ focusFromIndex(index, -7);
101
+ return;
102
+ }
103
+
104
+ if (event.key === 'Home') {
105
+ event.preventDefault();
106
+ focusFirstAvailable();
107
+ return;
108
+ }
109
+
110
+ if (event.altKey && event.key === 'PageUp') {
111
+ event.preventDefault();
112
+ for (let i = 0; i < 12; i += 1) {
113
+ onPrev?.();
114
+ }
115
+ return;
116
+ }
117
+
118
+ if (event.altKey && event.key === 'PageDown') {
119
+ event.preventDefault();
120
+ for (let i = 0; i < 12; i += 1) {
121
+ onNext?.();
122
+ }
123
+ return;
124
+ }
125
+
126
+ if (event.key === 'PageUp') {
127
+ event.preventDefault();
128
+ onPrev?.();
129
+ return;
130
+ }
131
+
132
+ if (event.key === 'PageDown') {
133
+ event.preventDefault();
134
+ onNext?.();
135
+ }
136
+ }
137
+ </script>
138
+
139
+ <div class="w-full min-w-[16.5rem] max-w-[22rem] rounded border border-surface-300-700 bg-surface-100-900 p-2">
140
+ <div class="mb-2 flex items-center justify-between">
141
+ <button
142
+ type="button"
143
+ class="btn btn-sm preset-tonal-surface-500"
144
+ aria-label="Previous month"
145
+ onclick={() => onPrev?.()}
146
+ >
147
+
148
+ </button>
149
+ <p class="text-sm font-semibold" aria-live="polite" aria-atomic="true">{monthLabel}</p>
150
+ <button
151
+ type="button"
152
+ class="btn btn-sm preset-tonal-surface-500"
153
+ aria-label="Next month"
154
+ onclick={() => onNext?.()}
155
+ >
156
+
157
+ </button>
158
+ </div>
159
+
160
+ <div class="mb-1 grid grid-cols-7 gap-1 text-center text-xs font-medium text-surface-500-400-token">
161
+ {#each WEEKDAY_LABELS as weekday}
162
+ <div>{weekday}</div>
163
+ {/each}
164
+ </div>
165
+
166
+ <div class="grid min-h-[15.5rem] grid-cols-7 gap-1" role="grid" aria-label={`Calendar month ${monthLabel}`}>
167
+ {#each cells as cell, i (cell.isoDate)}
168
+ {@const disabled = isCellDisabled(cell)}
169
+ {@const selected = selectedIso === cell.isoDate}
170
+ <button
171
+ type="button"
172
+ data-iso={cell.isoDate}
173
+ data-calendar-day="true"
174
+ role="gridcell"
175
+ class="h-8 rounded px-1 py-1 text-sm transition-colors sm:h-9"
176
+ class:text-surface-500-400-token={!cell.inCurrentMonth}
177
+ class:opacity-40={disabled}
178
+ class:hover:bg-surface-200-800={!disabled && !selected}
179
+ class:ring-1={cell.isToday && !selected}
180
+ class:ring-primary-500={cell.isToday && !selected}
181
+ class:bg-primary-500={selected}
182
+ class:text-white={selected}
183
+ aria-selected={selected}
184
+ disabled={disabled}
185
+ aria-label={`Select ${cell.isoDate}`}
186
+ onclick={() => onSelect?.(cell.isoDate)}
187
+ onkeydown={(event) => handleDayKeydown(event, i, cell)}
188
+ >
189
+ {cell.day}
190
+ </button>
191
+ {/each}
192
+ </div>
193
+ </div>
@@ -0,0 +1,13 @@
1
+ import { type CalendarCell } from './DateTimeCtrl.utils';
2
+ interface Props {
3
+ cells: CalendarCell[];
4
+ isDateSelectable?: (isoDate: string) => boolean;
5
+ monthLabel: string;
6
+ onNext?: () => void;
7
+ onPrev?: () => void;
8
+ onSelect?: (isoDate: string) => void;
9
+ selectedIso?: string;
10
+ }
11
+ declare const DateTimeCtrlCalendar: import("svelte").Component<Props, {}, "">;
12
+ type DateTimeCtrlCalendar = ReturnType<typeof DateTimeCtrlCalendar>;
13
+ export default DateTimeCtrlCalendar;
@@ -0,0 +1,119 @@
1
+ <script lang="ts">
2
+ import DateTimeCtrlCalendar from './DateTimeCtrlCalendar.svelte';
3
+ import DateTimeCtrlTimeFields from './DateTimeCtrlTimeFields.svelte';
4
+ import type { CalendarCell } from './DateTimeCtrl.utils';
5
+
6
+ interface Props {
7
+ amLabel?: string;
8
+ calendarCells?: CalendarCell[];
9
+ calendarMonthLabel?: string;
10
+ hour?: number;
11
+ hoursStep?: number;
12
+ isDateSelectable?: (isoDate: string) => boolean;
13
+ maxDate?: string;
14
+ maxTime?: string;
15
+ minDate?: string;
16
+ minTime?: string;
17
+ minute?: number;
18
+ minutesStep?: number;
19
+ onNow?: () => void;
20
+ onClose?: () => void;
21
+ onSelectCalendarDate?: (isoDate: string) => void;
22
+ onShiftMonth?: (delta: number) => void;
23
+ onTimeChange?: (value: { hour: number; minute: number; second: number }) => void;
24
+ pmLabel?: string;
25
+ presets?: Array<string | { label: string; value: string }>;
26
+ second?: number;
27
+ secondsStep?: number;
28
+ selectedDateIso?: string;
29
+ showDatePicker?: boolean;
30
+ showTimePicker?: boolean;
31
+ use12Hour?: boolean;
32
+ withTimeDropdown?: boolean;
33
+ }
34
+
35
+ let {
36
+ amLabel = 'AM',
37
+ calendarCells = [],
38
+ calendarMonthLabel = '',
39
+ hour = 0,
40
+ hoursStep = 1,
41
+ isDateSelectable,
42
+ maxDate,
43
+ maxTime,
44
+ minDate,
45
+ minTime,
46
+ minute = 0,
47
+ minutesStep = 1,
48
+ onNow,
49
+ onClose,
50
+ onSelectCalendarDate,
51
+ onShiftMonth,
52
+ onTimeChange,
53
+ pmLabel = 'PM',
54
+ presets = [],
55
+ second = 0,
56
+ secondsStep = 1,
57
+ selectedDateIso,
58
+ showDatePicker = false,
59
+ showTimePicker = false,
60
+ use12Hour = false,
61
+ withTimeDropdown = false,
62
+ }: Props = $props();
63
+ </script>
64
+
65
+ <div class="flex flex-col gap-3">
66
+ {#if showDatePicker}
67
+ <DateTimeCtrlCalendar
68
+ cells={calendarCells}
69
+ monthLabel={calendarMonthLabel}
70
+ {isDateSelectable}
71
+ selectedIso={selectedDateIso}
72
+ onPrev={() => onShiftMonth?.(-1)}
73
+ onNext={() => onShiftMonth?.(1)}
74
+ onSelect={onSelectCalendarDate}
75
+ />
76
+
77
+ {#if minDate || maxDate}
78
+ <p class="text-xs text-surface-500-400-token">
79
+ Allowed date: <code>{minDate ?? 'any'}</code> to <code>{maxDate ?? 'any'}</code>
80
+ </p>
81
+ {/if}
82
+ {/if}
83
+
84
+ {#if showTimePicker}
85
+ <DateTimeCtrlTimeFields
86
+ {hour}
87
+ {minute}
88
+ {second}
89
+ {minTime}
90
+ {maxTime}
91
+ {use12Hour}
92
+ withDropdown={withTimeDropdown}
93
+ {hoursStep}
94
+ {minutesStep}
95
+ {secondsStep}
96
+ {amLabel}
97
+ {pmLabel}
98
+ {presets}
99
+ onChange={onTimeChange}
100
+ />
101
+ {/if}
102
+
103
+ <div class="flex items-center justify-between gap-2">
104
+ <button
105
+ type="button"
106
+ class="btn btn-sm preset-filled-primary-500"
107
+ onclick={() => onNow?.()}
108
+ >
109
+ Now
110
+ </button>
111
+ <button
112
+ type="button"
113
+ class="btn btn-sm preset-tonal-surface-500"
114
+ onclick={() => onClose?.()}
115
+ >
116
+ Close
117
+ </button>
118
+ </div>
119
+ </div>
@@ -0,0 +1,39 @@
1
+ import type { CalendarCell } from './DateTimeCtrl.utils';
2
+ interface Props {
3
+ amLabel?: string;
4
+ calendarCells?: CalendarCell[];
5
+ calendarMonthLabel?: string;
6
+ hour?: number;
7
+ hoursStep?: number;
8
+ isDateSelectable?: (isoDate: string) => boolean;
9
+ maxDate?: string;
10
+ maxTime?: string;
11
+ minDate?: string;
12
+ minTime?: string;
13
+ minute?: number;
14
+ minutesStep?: number;
15
+ onNow?: () => void;
16
+ onClose?: () => void;
17
+ onSelectCalendarDate?: (isoDate: string) => void;
18
+ onShiftMonth?: (delta: number) => void;
19
+ onTimeChange?: (value: {
20
+ hour: number;
21
+ minute: number;
22
+ second: number;
23
+ }) => void;
24
+ pmLabel?: string;
25
+ presets?: Array<string | {
26
+ label: string;
27
+ value: string;
28
+ }>;
29
+ second?: number;
30
+ secondsStep?: number;
31
+ selectedDateIso?: string;
32
+ showDatePicker?: boolean;
33
+ showTimePicker?: boolean;
34
+ use12Hour?: boolean;
35
+ withTimeDropdown?: boolean;
36
+ }
37
+ declare const DateTimeCtrlPickerPanel: import("svelte").Component<Props, {}, "">;
38
+ type DateTimeCtrlPickerPanel = ReturnType<typeof DateTimeCtrlPickerPanel>;
39
+ export default DateTimeCtrlPickerPanel;
@@ -0,0 +1,343 @@
1
+ <script lang="ts">
2
+ interface TimePresetOption {
3
+ label: string;
4
+ value: string;
5
+ }
6
+
7
+ interface Props {
8
+ amLabel?: string;
9
+ hour?: number;
10
+ hoursStep?: number;
11
+ maxTime?: string;
12
+ minTime?: string;
13
+ minute?: number;
14
+ minutesStep?: number;
15
+ onChange?: (value: { hour: number; minute: number; second: number }) => void;
16
+ pmLabel?: string;
17
+ presets?: Array<string | TimePresetOption>;
18
+ second?: number;
19
+ secondsStep?: number;
20
+ use12Hour?: boolean;
21
+ withDropdown?: boolean;
22
+ }
23
+
24
+ let {
25
+ amLabel = 'AM',
26
+ hour = 0,
27
+ hoursStep = 1,
28
+ maxTime,
29
+ minTime,
30
+ minute = 0,
31
+ minutesStep = 1,
32
+ onChange,
33
+ pmLabel = 'PM',
34
+ presets = [],
35
+ second = 0,
36
+ secondsStep = 1,
37
+ use12Hour = false,
38
+ withDropdown = false,
39
+ }: Props = $props();
40
+
41
+ let h = $state('0');
42
+ let m = $state('0');
43
+ let s = $state('0');
44
+ let meridian = $state<'am' | 'pm'>('am');
45
+
46
+ const safeHoursStep = $derived(Math.max(1, Math.trunc(hoursStep || 1)));
47
+ const safeMinutesStep = $derived(Math.max(1, Math.trunc(minutesStep || 1)));
48
+ const safeSecondsStep = $derived(Math.max(1, Math.trunc(secondsStep || 1)));
49
+
50
+ const hourOptions = $derived.by(() => {
51
+ const options: number[] = [];
52
+ if (use12Hour) {
53
+ for (let i = 1; i <= 12; i += safeHoursStep) {
54
+ options.push(i);
55
+ }
56
+ if (!options.includes(12)) {
57
+ options.push(12);
58
+ }
59
+ return options;
60
+ }
61
+
62
+ for (let i = 0; i <= 23; i += safeHoursStep) {
63
+ options.push(i);
64
+ }
65
+ if (!options.includes(23)) {
66
+ options.push(23);
67
+ }
68
+
69
+ return options;
70
+ });
71
+
72
+ const minuteOptions = $derived.by(() => {
73
+ const options: number[] = [];
74
+ for (let i = 0; i <= 59; i += safeMinutesStep) {
75
+ options.push(i);
76
+ }
77
+ if (!options.includes(59)) {
78
+ options.push(59);
79
+ }
80
+ return options;
81
+ });
82
+
83
+ const secondOptions = $derived.by(() => {
84
+ const options: number[] = [];
85
+ for (let i = 0; i <= 59; i += safeSecondsStep) {
86
+ options.push(i);
87
+ }
88
+ if (!options.includes(59)) {
89
+ options.push(59);
90
+ }
91
+ return options;
92
+ });
93
+
94
+ const normalizedPresets = $derived.by(() => {
95
+ return presets
96
+ .map((preset) => {
97
+ if (typeof preset === 'string') {
98
+ return { label: preset, value: preset };
99
+ }
100
+
101
+ return preset;
102
+ })
103
+ .filter((preset) => preset.value && preset.value.trim() !== '');
104
+ });
105
+
106
+ function pad2(value: number): string {
107
+ return String(value).padStart(2, '0');
108
+ }
109
+
110
+ function clamp(value: number, min: number, max: number): number {
111
+ return Math.max(min, Math.min(max, value));
112
+ }
113
+
114
+ function to12Hour(inputHour: number): { displayHour: number; meridian: 'am' | 'pm' } {
115
+ const isPm = inputHour >= 12;
116
+ const displayHour = inputHour % 12 === 0 ? 12 : inputHour % 12;
117
+ return { displayHour, meridian: isPm ? 'pm' : 'am' };
118
+ }
119
+
120
+ function from12Hour(displayHour: number, nextMeridian: 'am' | 'pm'): number {
121
+ const normalized = clamp(displayHour, 1, 12) % 12;
122
+ if (nextMeridian === 'pm') {
123
+ return normalized + 12;
124
+ }
125
+ return normalized;
126
+ }
127
+
128
+ function parsePreset(value: string): { hour: number; minute: number; second: number } | null {
129
+ const matched = /^(\d{1,2}):(\d{2})(?::(\d{2}))?$/.exec(value.trim());
130
+ if (!matched) {
131
+ return null;
132
+ }
133
+
134
+ const parsedHour = Number(matched[1]);
135
+ const parsedMinute = Number(matched[2]);
136
+ const parsedSecond = Number(matched[3] ?? '0');
137
+
138
+ if (
139
+ !Number.isInteger(parsedHour) ||
140
+ !Number.isInteger(parsedMinute) ||
141
+ !Number.isInteger(parsedSecond) ||
142
+ parsedHour < 0 ||
143
+ parsedHour > 23 ||
144
+ parsedMinute < 0 ||
145
+ parsedMinute > 59 ||
146
+ parsedSecond < 0 ||
147
+ parsedSecond > 59
148
+ ) {
149
+ return null;
150
+ }
151
+
152
+ return {
153
+ hour: parsedHour,
154
+ minute: parsedMinute,
155
+ second: parsedSecond,
156
+ };
157
+ }
158
+
159
+ function emit(): void {
160
+ const parsedHour = Number(h);
161
+ const parsedMinute = Number(m);
162
+ const parsedSecond = Number(s);
163
+
164
+ if (
165
+ !Number.isFinite(parsedHour) ||
166
+ !Number.isFinite(parsedMinute) ||
167
+ !Number.isFinite(parsedSecond)
168
+ ) {
169
+ return;
170
+ }
171
+
172
+ const minuteValue = clamp(Math.trunc(parsedMinute), 0, 59);
173
+ const secondValue = clamp(Math.trunc(parsedSecond), 0, 59);
174
+
175
+ let hourValue = clamp(Math.trunc(parsedHour), 0, 23);
176
+ if (use12Hour) {
177
+ hourValue = from12Hour(parsedHour, meridian);
178
+ }
179
+
180
+ onChange?.({
181
+ hour: hourValue,
182
+ minute: minuteValue,
183
+ second: secondValue,
184
+ });
185
+ }
186
+
187
+ function applyPreset(preset: string): void {
188
+ const parsed = parsePreset(preset);
189
+ if (!parsed) {
190
+ return;
191
+ }
192
+
193
+ if (use12Hour) {
194
+ const converted = to12Hour(parsed.hour);
195
+ h = String(converted.displayHour);
196
+ meridian = converted.meridian;
197
+ } else {
198
+ h = String(parsed.hour);
199
+ }
200
+
201
+ m = String(parsed.minute);
202
+ s = String(parsed.second);
203
+ emit();
204
+ }
205
+
206
+ $effect(() => {
207
+ if (use12Hour) {
208
+ const converted = to12Hour(hour);
209
+ h = String(converted.displayHour);
210
+ meridian = converted.meridian;
211
+ } else {
212
+ h = String(hour);
213
+ }
214
+
215
+ m = String(minute);
216
+ s = String(second);
217
+ });
218
+ </script>
219
+
220
+ <div class="rounded border border-surface-300-700 bg-surface-100-900 p-2">
221
+ {#if withDropdown}
222
+ <div class="grid grid-cols-4 gap-2">
223
+ <label class="flex flex-col gap-1">
224
+ <span class="text-xs text-surface-500-400-token">Hour</span>
225
+ <select class="select" bind:value={h} onchange={emit}>
226
+ {#each hourOptions as option}
227
+ <option value={String(option)}>{pad2(option)}</option>
228
+ {/each}
229
+ </select>
230
+ </label>
231
+
232
+ <label class="flex flex-col gap-1">
233
+ <span class="text-xs text-surface-500-400-token">Minute</span>
234
+ <select class="select" bind:value={m} onchange={emit}>
235
+ {#each minuteOptions as option}
236
+ <option value={String(option)}>{pad2(option)}</option>
237
+ {/each}
238
+ </select>
239
+ </label>
240
+
241
+ <label class="flex flex-col gap-1">
242
+ <span class="text-xs text-surface-500-400-token">Second</span>
243
+ <select class="select" bind:value={s} onchange={emit}>
244
+ {#each secondOptions as option}
245
+ <option value={String(option)}>{pad2(option)}</option>
246
+ {/each}
247
+ </select>
248
+ </label>
249
+
250
+ {#if use12Hour}
251
+ <label class="flex flex-col gap-1">
252
+ <span class="text-xs text-surface-500-400-token">Period</span>
253
+ <select
254
+ class="select"
255
+ bind:value={meridian}
256
+ onchange={emit}
257
+ aria-label="AM PM selector"
258
+ >
259
+ <option value="am">{amLabel}</option>
260
+ <option value="pm">{pmLabel}</option>
261
+ </select>
262
+ </label>
263
+ {/if}
264
+ </div>
265
+ {:else}
266
+ <div class="grid grid-cols-4 gap-2">
267
+ <label class="flex flex-col gap-1">
268
+ <span class="text-xs text-surface-500-400-token">Hour</span>
269
+ <input
270
+ class="input"
271
+ type="number"
272
+ min={use12Hour ? '1' : '0'}
273
+ max={use12Hour ? '12' : '23'}
274
+ step={String(safeHoursStep)}
275
+ bind:value={h}
276
+ oninput={emit}
277
+ />
278
+ </label>
279
+
280
+ <label class="flex flex-col gap-1">
281
+ <span class="text-xs text-surface-500-400-token">Minute</span>
282
+ <input
283
+ class="input"
284
+ type="number"
285
+ min="0"
286
+ max="59"
287
+ step={String(safeMinutesStep)}
288
+ bind:value={m}
289
+ oninput={emit}
290
+ />
291
+ </label>
292
+
293
+ <label class="flex flex-col gap-1">
294
+ <span class="text-xs text-surface-500-400-token">Second</span>
295
+ <input
296
+ class="input"
297
+ type="number"
298
+ min="0"
299
+ max="59"
300
+ step={String(safeSecondsStep)}
301
+ bind:value={s}
302
+ oninput={emit}
303
+ />
304
+ </label>
305
+
306
+ {#if use12Hour}
307
+ <label class="flex flex-col gap-1">
308
+ <span class="text-xs text-surface-500-400-token">Period</span>
309
+ <select
310
+ class="select"
311
+ bind:value={meridian}
312
+ onchange={emit}
313
+ aria-label="AM PM selector"
314
+ >
315
+ <option value="am">{amLabel}</option>
316
+ <option value="pm">{pmLabel}</option>
317
+ </select>
318
+ </label>
319
+ {/if}
320
+ </div>
321
+ {/if}
322
+
323
+ {#if normalizedPresets.length > 0}
324
+ <div class="mt-3 flex flex-wrap gap-2">
325
+ {#each normalizedPresets as preset (preset.value)}
326
+ <button
327
+ type="button"
328
+ class="btn btn-sm preset-tonal-surface-500"
329
+ onclick={() => applyPreset(preset.value)}
330
+ title={`Apply ${preset.label}`}
331
+ >
332
+ {preset.label}
333
+ </button>
334
+ {/each}
335
+ </div>
336
+ {/if}
337
+
338
+ {#if minTime || maxTime}
339
+ <p class="mt-2 text-xs text-surface-500-400-token">
340
+ Allowed time: <code>{minTime ?? '00:00:00'}</code> to <code>{maxTime ?? '23:59:59'}</code>
341
+ </p>
342
+ {/if}
343
+ </div>