@warkypublic/svelix 0.1.44 → 0.1.47

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 (109) hide show
  1. package/README.md +93 -33
  2. package/dist/components/BetterMenu/BetterMenu.svelte +21 -37
  3. package/dist/components/BetterMenu/BetterMenuAsyncButton.svelte +21 -31
  4. package/dist/components/BetterMenu/BetterMenuPreview.svelte +27 -29
  5. package/dist/components/BetterMenu/MenuRenderer.svelte +40 -51
  6. package/dist/components/Boxer/Boxer.svelte +488 -602
  7. package/dist/components/Boxer/BoxerTarget.svelte +6 -46
  8. package/dist/components/Boxer/BoxerTarget.svelte.d.ts +1 -0
  9. package/dist/components/Boxer/types.d.ts +0 -1
  10. package/dist/components/Button.svelte +19 -43
  11. package/dist/components/ButtonPreview.svelte +2 -11
  12. package/dist/components/CardGrid/CardGrid.svelte +1141 -0
  13. package/dist/components/CardGrid/CardGrid.svelte.d.ts +61 -0
  14. package/dist/components/CardGrid/CardGridFilterPanel.svelte +320 -0
  15. package/dist/components/CardGrid/CardGridFilterPanel.svelte.d.ts +11 -0
  16. package/dist/components/CardGrid/DefaultCard.svelte +287 -0
  17. package/dist/components/CardGrid/DefaultCard.svelte.d.ts +10 -0
  18. package/dist/components/CardGrid/ImageCardStory.svelte +308 -0
  19. package/dist/components/CardGrid/ImageCardStory.svelte.d.ts +18 -0
  20. package/dist/components/CardGrid/cardGrid.d.ts +33 -0
  21. package/dist/components/CardGrid/cardGrid.js +21 -0
  22. package/dist/components/CardGrid/index.d.ts +4 -0
  23. package/dist/components/CardGrid/index.js +4 -0
  24. package/dist/components/ContentEditor/CollaboraInsertTextDemo.svelte +5 -10
  25. package/dist/components/ContentEditor/ContentEditor.svelte +24 -50
  26. package/dist/components/ContentEditor/subcomponents/AudioPlayer.svelte +17 -17
  27. package/dist/components/ContentEditor/subcomponents/CollaboraEditor.svelte +244 -309
  28. package/dist/components/ContentEditor/subcomponents/EmailViewer.svelte +2 -3
  29. package/dist/components/ContentEditor/subcomponents/ImageViewer.svelte +14 -15
  30. package/dist/components/ContentEditor/subcomponents/MarkdownViewer.svelte +74 -90
  31. package/dist/components/ContentEditor/subcomponents/MonacoEditor.svelte +75 -103
  32. package/dist/components/ContentEditor/subcomponents/Office365Editor.svelte +234 -297
  33. package/dist/components/ContentEditor/subcomponents/PdfViewer.svelte +14 -20
  34. package/dist/components/ContentEditor/subcomponents/TextEditor.svelte +122 -150
  35. package/dist/components/ContentEditor/subcomponents/UnknownFile.svelte +105 -126
  36. package/dist/components/ContentEditor/subcomponents/VideoPlayer.svelte +17 -17
  37. package/dist/components/ContentEditor/subcomponents/ZipViewer.svelte +2 -3
  38. package/dist/components/ErrorBoundary/ErrorBoundary.svelte +41 -58
  39. package/dist/components/ErrorBoundary/ErrorBoundaryPreview.svelte +8 -16
  40. package/dist/components/Former/Former.svelte +306 -424
  41. package/dist/components/Former/FormerButtonArea.svelte +17 -68
  42. package/dist/components/Former/FormerDrawer.svelte +26 -78
  43. package/dist/components/Former/FormerDrawerPreview.svelte +99 -89
  44. package/dist/components/Former/FormerModal.svelte +24 -70
  45. package/dist/components/Former/FormerModalPreview.svelte +99 -89
  46. package/dist/components/Former/FormerPreview.svelte +76 -78
  47. package/dist/components/Former/FormerRestApiPreview.svelte +55 -48
  48. package/dist/components/FormerControllers/ButtonCtrl.svelte +13 -44
  49. package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrl.svelte +280 -445
  50. package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlCalendar.svelte +95 -136
  51. package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlPickerPanel.svelte +3 -62
  52. package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlTimeFields.svelte +143 -217
  53. package/dist/components/FormerControllers/IconButtonCtrl.svelte +13 -42
  54. package/dist/components/FormerControllers/InlineWrapper.svelte +12 -41
  55. package/dist/components/FormerControllers/InlineWrapperPreview.svelte +4 -26
  56. package/dist/components/FormerControllers/NativeSelectCtrl.svelte +3 -29
  57. package/dist/components/FormerControllers/NumberInputCtrl.svelte +9 -35
  58. package/dist/components/FormerControllers/PasswordInputCtrl.svelte +4 -23
  59. package/dist/components/FormerControllers/SwitchCtrl.svelte +3 -20
  60. package/dist/components/FormerControllers/TextAreaCtrl.svelte +3 -24
  61. package/dist/components/FormerControllers/TextInputCtrl.svelte +3 -24
  62. package/dist/components/GlobalStateStore/GlobalStateStoreProvider.svelte +73 -120
  63. package/dist/components/Gridler/components/Gridler.svelte +338 -442
  64. package/dist/components/Gridler/components/GridlerCanvas.svelte +720 -1115
  65. package/dist/components/Gridler/components/GridlerEditor.svelte +2 -15
  66. package/dist/components/Gridler/components/GridlerFull.svelte +520 -841
  67. package/dist/components/Gridler/components/GridlerFull.svelte.d.ts +1 -1
  68. package/dist/components/Gridler/components/GridlerFullWithFormerPreview.svelte +194 -147
  69. package/dist/components/Gridler/components/GridlerSearch.svelte +7 -16
  70. package/dist/components/Gridler/components/GridlerSearchToggle.svelte +7 -19
  71. package/dist/components/Gridler/types.d.ts +1 -1
  72. package/dist/components/Icons/IconAdd.svelte +1 -3
  73. package/dist/components/Icons/IconAlertCircle.svelte +1 -3
  74. package/dist/components/Icons/IconAlertTriangle.svelte +1 -3
  75. package/dist/components/Icons/IconCamera.svelte +1 -3
  76. package/dist/components/Icons/IconClose.svelte +1 -3
  77. package/dist/components/Icons/IconEdit.svelte +1 -3
  78. package/dist/components/Icons/IconFilter.svelte +1 -3
  79. package/dist/components/Icons/IconSearch.svelte +1 -3
  80. package/dist/components/Icons/IconSort.svelte +1 -3
  81. package/dist/components/Icons/IconTrash.svelte +1 -3
  82. package/dist/components/Portal/Portal.svelte +14 -38
  83. package/dist/components/Screenshot/Screenshot.svelte +7 -13
  84. package/dist/components/Svark/Svark.svelte +680 -852
  85. package/dist/components/Svark/SvarkContextMenu.svelte +44 -63
  86. package/dist/components/Svark/SvarkInfiniteLoader.svelte +2 -6
  87. package/dist/components/Svark/SvarkPager.svelte +2 -28
  88. package/dist/components/Svark/SvarkSelectionDemo.svelte +4 -8
  89. package/dist/components/Svark/SvarkTopBar.svelte +9 -74
  90. package/dist/components/SvarkGrid/SvarkGrid.svelte +877 -1123
  91. package/dist/components/SvarkGrid/components/SvarkColumnFilterForm.svelte +45 -58
  92. package/dist/components/SvarkGrid/components/SvarkContextMenu.svelte +44 -63
  93. package/dist/components/SvarkGrid/components/SvarkHeaderFilterCell.svelte +41 -75
  94. package/dist/components/SvarkGrid/components/SvarkInfiniteLoader.svelte +2 -6
  95. package/dist/components/SvarkGrid/components/SvarkPager.svelte +2 -28
  96. package/dist/components/SvarkGrid/components/SvarkSearchPopover.svelte +8 -16
  97. package/dist/components/SvarkGrid/components/SvarkTopBar.svelte +6 -62
  98. package/dist/components/SvarkGrid/internal/SvarkGridView.svelte +345 -507
  99. package/dist/components/VTree/VTree.svelte +349 -431
  100. package/dist/components/VTree/VTreeContextMenu.svelte +21 -33
  101. package/dist/components/VTree/VTreeEventsDemo.svelte +48 -58
  102. package/dist/components/VTree/VTreeRow.svelte +13 -65
  103. package/dist/components/VTree/VTreeSearch.svelte +6 -13
  104. package/dist/components/VTree/VTreeVirtualViewport.svelte +37 -106
  105. package/dist/components/index.d.ts +1 -0
  106. package/dist/components/index.js +1 -0
  107. package/llm/COMPONENT_GUIDE.md +240 -0
  108. package/llm/plans/card_grid.plan.md +305 -0
  109. package/package.json +48 -46
@@ -1,139 +1,98 @@
1
- <script lang="ts">
2
- import { WEEKDAY_LABELS, type CalendarCell } from './DateTimeCtrl.utils';
3
-
4
- export 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
- }
1
+ <script lang="ts">import { WEEKDAY_LABELS } from "./DateTimeCtrl.utils";
2
+ let { cells, isDateSelectable, monthLabel, onNext, onPrev, onSelect, selectedIso } = $props();
3
+ function isCellDisabled(cell) {
4
+ if (!cell.inCurrentMonth) {
5
+ return true;
6
+ }
7
+ if (!isDateSelectable) {
8
+ return false;
9
+ }
10
+ return !isDateSelectable(cell.isoDate);
11
+ }
12
+ function focusFirstAvailable() {
13
+ if (typeof document === "undefined") {
14
+ return;
15
+ }
16
+ const first = cells.find((cell) => !isCellDisabled(cell));
17
+ if (!first) {
18
+ return;
19
+ }
20
+ const target = document.querySelector(`button[data-iso="${first.isoDate}"]`);
21
+ target?.focus();
22
+ }
23
+ function focusFromIndex(startIndex, step) {
24
+ if (typeof document === "undefined") {
25
+ return;
26
+ }
27
+ let index = startIndex + step;
28
+ while (index >= 0 && index < cells.length) {
29
+ const nextCell = cells[index];
30
+ if (!isCellDisabled(nextCell)) {
31
+ const target = document.querySelector(`button[data-iso="${nextCell.isoDate}"]`);
32
+ target?.focus();
33
+ return;
34
+ }
35
+ index += step;
36
+ }
37
+ }
38
+ function handleDayKeydown(event, index, cell) {
39
+ if (isCellDisabled(cell)) {
40
+ return;
41
+ }
42
+ if (event.key === "Enter" || event.key === " ") {
43
+ event.preventDefault();
44
+ onSelect?.(cell.isoDate);
45
+ return;
46
+ }
47
+ if (event.key === "ArrowRight") {
48
+ event.preventDefault();
49
+ focusFromIndex(index, 1);
50
+ return;
51
+ }
52
+ if (event.key === "ArrowLeft") {
53
+ event.preventDefault();
54
+ focusFromIndex(index, -1);
55
+ return;
56
+ }
57
+ if (event.key === "ArrowDown") {
58
+ event.preventDefault();
59
+ focusFromIndex(index, 7);
60
+ return;
61
+ }
62
+ if (event.key === "ArrowUp") {
63
+ event.preventDefault();
64
+ focusFromIndex(index, -7);
65
+ return;
66
+ }
67
+ if (event.key === "Home") {
68
+ event.preventDefault();
69
+ focusFirstAvailable();
70
+ return;
71
+ }
72
+ if (event.altKey && event.key === "PageUp") {
73
+ event.preventDefault();
74
+ for (let i = 0; i < 12; i += 1) {
75
+ onPrev?.();
76
+ }
77
+ return;
78
+ }
79
+ if (event.altKey && event.key === "PageDown") {
80
+ event.preventDefault();
81
+ for (let i = 0; i < 12; i += 1) {
82
+ onNext?.();
83
+ }
84
+ return;
85
+ }
86
+ if (event.key === "PageUp") {
87
+ event.preventDefault();
88
+ onPrev?.();
89
+ return;
90
+ }
91
+ if (event.key === "PageDown") {
92
+ event.preventDefault();
93
+ onNext?.();
94
+ }
95
+ }
137
96
  </script>
138
97
 
139
98
  <div class="w-full min-w-[16.5rem] max-w-[22rem] rounded border border-surface-300-700 bg-surface-100-900 p-2">
@@ -1,65 +1,6 @@
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
- export 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();
1
+ <script lang="ts">import DateTimeCtrlCalendar from "./DateTimeCtrlCalendar.svelte";
2
+ import DateTimeCtrlTimeFields from "./DateTimeCtrlTimeFields.svelte";
3
+ let { amLabel = "AM", calendarCells = [], calendarMonthLabel = "", hour = 0, hoursStep = 1, isDateSelectable, maxDate, maxTime, minDate, minTime, minute = 0, minutesStep = 1, onNow, onClose, onSelectCalendarDate, onShiftMonth, onTimeChange, pmLabel = "PM", presets = [], second = 0, secondsStep = 1, selectedDateIso, showDatePicker = false, showTimePicker = false, use12Hour = false, withTimeDropdown = false } = $props();
63
4
  </script>
64
5
 
65
6
  <div class="flex flex-col gap-3">
@@ -1,220 +1,146 @@
1
- <script lang="ts">
2
- interface TimePresetOption {
3
- label: string;
4
- value: string;
5
- }
6
-
7
- export 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
- });
1
+ <script lang="ts">let { amLabel = "AM", hour = 0, hoursStep = 1, maxTime, minTime, minute = 0, minutesStep = 1, onChange, pmLabel = "PM", presets = [], second = 0, secondsStep = 1, use12Hour = false, withDropdown = false } = $props();
2
+ let h = $state("0");
3
+ let m = $state("0");
4
+ let s = $state("0");
5
+ let meridian = $state("am");
6
+ const safeHoursStep = $derived(Math.max(1, Math.trunc(hoursStep || 1)));
7
+ const safeMinutesStep = $derived(Math.max(1, Math.trunc(minutesStep || 1)));
8
+ const safeSecondsStep = $derived(Math.max(1, Math.trunc(secondsStep || 1)));
9
+ const hourOptions = $derived.by(() => {
10
+ const options = [];
11
+ if (use12Hour) {
12
+ for (let i = 1; i <= 12; i += safeHoursStep) {
13
+ options.push(i);
14
+ }
15
+ if (!options.includes(12)) {
16
+ options.push(12);
17
+ }
18
+ return options;
19
+ }
20
+ for (let i = 0; i <= 23; i += safeHoursStep) {
21
+ options.push(i);
22
+ }
23
+ if (!options.includes(23)) {
24
+ options.push(23);
25
+ }
26
+ return options;
27
+ });
28
+ const minuteOptions = $derived.by(() => {
29
+ const options = [];
30
+ for (let i = 0; i <= 59; i += safeMinutesStep) {
31
+ options.push(i);
32
+ }
33
+ if (!options.includes(59)) {
34
+ options.push(59);
35
+ }
36
+ return options;
37
+ });
38
+ const secondOptions = $derived.by(() => {
39
+ const options = [];
40
+ for (let i = 0; i <= 59; i += safeSecondsStep) {
41
+ options.push(i);
42
+ }
43
+ if (!options.includes(59)) {
44
+ options.push(59);
45
+ }
46
+ return options;
47
+ });
48
+ const normalizedPresets = $derived.by(() => {
49
+ return presets.map((preset) => {
50
+ if (typeof preset === "string") {
51
+ return {
52
+ label: preset,
53
+ value: preset
54
+ };
55
+ }
56
+ return preset;
57
+ }).filter((preset) => preset.value && preset.value.trim() !== "");
58
+ });
59
+ function pad2(value) {
60
+ return String(value).padStart(2, "0");
61
+ }
62
+ function clamp(value, min, max) {
63
+ return Math.max(min, Math.min(max, value));
64
+ }
65
+ function to12Hour(inputHour) {
66
+ const isPm = inputHour >= 12;
67
+ const displayHour = inputHour % 12 === 0 ? 12 : inputHour % 12;
68
+ return {
69
+ displayHour,
70
+ meridian: isPm ? "pm" : "am"
71
+ };
72
+ }
73
+ function from12Hour(displayHour, nextMeridian) {
74
+ const normalized = clamp(displayHour, 1, 12) % 12;
75
+ if (nextMeridian === "pm") {
76
+ return normalized + 12;
77
+ }
78
+ return normalized;
79
+ }
80
+ function parsePreset(value) {
81
+ const matched = /^(\d{1,2}):(\d{2})(?::(\d{2}))?$/.exec(value.trim());
82
+ if (!matched) {
83
+ return null;
84
+ }
85
+ const parsedHour = Number(matched[1]);
86
+ const parsedMinute = Number(matched[2]);
87
+ const parsedSecond = Number(matched[3] ?? "0");
88
+ if (!Number.isInteger(parsedHour) || !Number.isInteger(parsedMinute) || !Number.isInteger(parsedSecond) || parsedHour < 0 || parsedHour > 23 || parsedMinute < 0 || parsedMinute > 59 || parsedSecond < 0 || parsedSecond > 59) {
89
+ return null;
90
+ }
91
+ return {
92
+ hour: parsedHour,
93
+ minute: parsedMinute,
94
+ second: parsedSecond
95
+ };
96
+ }
97
+ function emit() {
98
+ const parsedHour = Number(h);
99
+ const parsedMinute = Number(m);
100
+ const parsedSecond = Number(s);
101
+ if (!Number.isFinite(parsedHour) || !Number.isFinite(parsedMinute) || !Number.isFinite(parsedSecond)) {
102
+ return;
103
+ }
104
+ const minuteValue = clamp(Math.trunc(parsedMinute), 0, 59);
105
+ const secondValue = clamp(Math.trunc(parsedSecond), 0, 59);
106
+ let hourValue = clamp(Math.trunc(parsedHour), 0, 23);
107
+ if (use12Hour) {
108
+ hourValue = from12Hour(parsedHour, meridian);
109
+ }
110
+ onChange?.({
111
+ hour: hourValue,
112
+ minute: minuteValue,
113
+ second: secondValue
114
+ });
115
+ }
116
+ function applyPreset(preset) {
117
+ const parsed = parsePreset(preset);
118
+ if (!parsed) {
119
+ return;
120
+ }
121
+ if (use12Hour) {
122
+ const converted = to12Hour(parsed.hour);
123
+ h = String(converted.displayHour);
124
+ meridian = converted.meridian;
125
+ } else {
126
+ h = String(parsed.hour);
127
+ }
128
+ m = String(parsed.minute);
129
+ s = String(parsed.second);
130
+ emit();
131
+ }
132
+ $effect(() => {
133
+ if (use12Hour) {
134
+ const converted = to12Hour(hour);
135
+ h = String(converted.displayHour);
136
+ meridian = converted.meridian;
137
+ } else {
138
+ h = String(hour);
139
+ }
140
+ m = String(minute);
141
+ s = String(second);
142
+ });
143
+ export {};
218
144
  </script>
219
145
 
220
146
  <div class="rounded border border-surface-300-700 bg-surface-100-900 p-2">