@svelte-atoms/core 1.0.0-alpha.29 → 1.0.0-alpha.31

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 (176) hide show
  1. package/README.md +3 -2
  2. package/dist/attachments/clickout.svelte.d.ts +1 -1
  3. package/dist/attachments/clickout.svelte.js +2 -2
  4. package/dist/components/accordion/accordion-root.svelte +65 -61
  5. package/dist/components/accordion/accordion-root.svelte.d.ts +1 -1
  6. package/dist/components/accordion/accordion.stories.svelte +70 -134
  7. package/dist/components/accordion/item/accordion-item-body.svelte +44 -42
  8. package/dist/components/accordion/item/accordion-item-header.svelte +51 -50
  9. package/dist/components/accordion/item/accordion-item-indicator.svelte +51 -50
  10. package/dist/components/accordion/item/accordion-item-root.svelte +66 -65
  11. package/dist/components/accordion/item/bond.svelte.d.ts +2 -0
  12. package/dist/components/accordion/item/index.d.ts +3 -0
  13. package/dist/components/accordion/item/index.js +3 -0
  14. package/dist/components/accordion/item/motion.svelte.d.ts +15 -0
  15. package/dist/components/accordion/item/motion.svelte.js +30 -0
  16. package/dist/components/accordion/item/types.d.ts +7 -24
  17. package/dist/components/alert/alert-close-button.svelte +66 -70
  18. package/dist/components/alert/alert-description.svelte +42 -42
  19. package/dist/components/alert/alert-description.svelte.d.ts +3 -6
  20. package/dist/components/alert/alert-root.svelte +68 -103
  21. package/dist/components/alert/alert-root.svelte.d.ts +2 -2
  22. package/dist/components/alert/alert.stories.svelte +10 -11
  23. package/dist/components/alert/bond.svelte.d.ts +0 -13
  24. package/dist/components/alert/bond.svelte.js +0 -32
  25. package/dist/components/alert/types.d.ts +8 -32
  26. package/dist/components/atom/html-atom.svelte +261 -207
  27. package/dist/components/avatar/avatar.stories.svelte +8 -13
  28. package/dist/components/badge/badge.stories.svelte +1 -6
  29. package/dist/components/badge/badge.svelte +1 -1
  30. package/dist/components/breadcrumb/breadcrumb.stories.svelte +16 -21
  31. package/dist/components/button/button.stories.svelte +1 -34
  32. package/dist/components/calendar/calendar-day.svelte +9 -4
  33. package/dist/components/calendar/calendar-header.svelte +29 -29
  34. package/dist/components/calendar/calendar-root.svelte +206 -206
  35. package/dist/components/calendar/calendar.stories.svelte +26 -31
  36. package/dist/components/card/card-body.svelte +1 -1
  37. package/dist/components/card/card-footer.svelte +1 -1
  38. package/dist/components/card/card-root.svelte +1 -1
  39. package/dist/components/card/card.stories.svelte +92 -104
  40. package/dist/components/checkbox/checkbox.stories.svelte +4 -9
  41. package/dist/components/checkbox/checkbox.svelte +159 -157
  42. package/dist/components/collapsible/collapsible.stories.svelte +2 -3
  43. package/dist/components/combobox/atoms.d.ts +1 -1
  44. package/dist/components/combobox/atoms.js +1 -1
  45. package/dist/components/combobox/combobox-root.svelte +1 -1
  46. package/dist/components/combobox/compobox.stories.svelte +19 -22
  47. package/dist/components/combobox/index.d.ts +1 -0
  48. package/dist/components/container/container.stories.svelte +8 -11
  49. package/dist/components/container/container.svelte.d.ts +1 -1
  50. package/dist/components/datagrid/datagrid-root.svelte +59 -59
  51. package/dist/components/datagrid/datagrid.css +5 -5
  52. package/dist/components/datagrid/datagrid.stories.svelte +47 -50
  53. package/dist/components/datagrid/tr/bond.svelte.d.ts +4 -2
  54. package/dist/components/datagrid/tr/bond.svelte.js +9 -7
  55. package/dist/components/datagrid/tr/datagrid-tr.svelte +90 -88
  56. package/dist/components/date-picker/date-picker-calendar.svelte +2 -2
  57. package/dist/components/date-picker/date-picker-header.svelte +100 -100
  58. package/dist/components/date-picker/date-picker-months.svelte +142 -142
  59. package/dist/components/date-picker/date-picker-root.svelte +95 -95
  60. package/dist/components/date-picker/date-picker-years.svelte +205 -205
  61. package/dist/components/date-picker/date-picker.stories.svelte +35 -42
  62. package/dist/components/dialog/bond.svelte.d.ts +13 -3
  63. package/dist/components/dialog/bond.svelte.js +66 -5
  64. package/dist/components/dialog/dialog-content.svelte +2 -20
  65. package/dist/components/dialog/dialog-root.svelte +91 -110
  66. package/dist/components/dialog/dialog.stories.svelte +34 -37
  67. package/dist/components/dialog/motion.svelte.d.ts +13 -0
  68. package/dist/components/dialog/motion.svelte.js +44 -0
  69. package/dist/components/drawer/attachments.svelte.d.ts +1 -1
  70. package/dist/components/drawer/attachments.svelte.js +7 -10
  71. package/dist/components/drawer/bond.svelte.d.ts +24 -5
  72. package/dist/components/drawer/bond.svelte.js +77 -11
  73. package/dist/components/drawer/drawer-content.svelte +49 -42
  74. package/dist/components/drawer/drawer.stories.svelte +144 -224
  75. package/dist/components/drawer/index.d.ts +2 -0
  76. package/dist/components/drawer/index.js +2 -0
  77. package/dist/components/drawer/motion.d.ts +15 -0
  78. package/dist/components/drawer/motion.js +28 -0
  79. package/dist/components/dropdown/atoms.d.ts +1 -1
  80. package/dist/components/dropdown/atoms.js +1 -1
  81. package/dist/components/dropdown/bond.svelte.d.ts +5 -1
  82. package/dist/components/dropdown/dropdown-root.svelte +1 -1
  83. package/dist/components/dropdown/dropdown.stories.svelte +38 -41
  84. package/dist/components/dropdown/index.d.ts +1 -0
  85. package/dist/components/form/form.stories.svelte +58 -61
  86. package/dist/components/image/image.stories.svelte +9 -12
  87. package/dist/components/input/input.stories.svelte +11 -14
  88. package/dist/components/label/label.stories.svelte +1 -12
  89. package/dist/components/label/label.stories.svelte.d.ts +24 -4
  90. package/dist/components/lazy/lazy.stories.svelte +28 -35
  91. package/dist/components/lazy/lazy.svelte +28 -28
  92. package/dist/components/link/link.stories.svelte +1 -12
  93. package/dist/components/link/link.stories.svelte.d.ts +24 -4
  94. package/dist/components/list/list-item.svelte +20 -20
  95. package/dist/components/menu/atoms.d.ts +1 -0
  96. package/dist/components/menu/atoms.js +1 -0
  97. package/dist/components/menu/index.d.ts +2 -1
  98. package/dist/components/menu/index.js +1 -1
  99. package/dist/components/menu/menu-item.svelte +69 -51
  100. package/dist/components/menu/menu-item.svelte.d.ts +1 -0
  101. package/dist/components/menu/menu-list.svelte +40 -40
  102. package/dist/components/menu/menu.stories.svelte +9 -12
  103. package/dist/components/popover/bond.svelte.d.ts +20 -7
  104. package/dist/components/popover/bond.svelte.js +104 -45
  105. package/dist/components/popover/motion.d.ts +6 -0
  106. package/dist/components/popover/motion.js +56 -0
  107. package/dist/components/popover/popover-arrow.svelte +4 -4
  108. package/dist/components/popover/popover-content.svelte +137 -178
  109. package/dist/components/popover/popover-indicator.svelte +2 -1
  110. package/dist/components/popover/popover-root.svelte +1 -1
  111. package/dist/components/popover/popover.stories.svelte +49 -52
  112. package/dist/components/popover/types.d.ts +9 -7
  113. package/dist/components/portal/active-portal.svelte +29 -22
  114. package/dist/components/portal/active-portal.svelte.d.ts +2 -9
  115. package/dist/components/portal/portal-root.svelte +76 -83
  116. package/dist/components/portal/portal-root.svelte.d.ts +4 -6
  117. package/dist/components/portal/teleport.svelte +49 -50
  118. package/dist/components/portal/teleport.svelte.d.ts +3 -4
  119. package/dist/components/qr-code/qr-code.stories.svelte +18 -27
  120. package/dist/components/qr-code/qr-code.svelte +75 -75
  121. package/dist/components/radio/radio-group.stories.svelte +21 -30
  122. package/dist/components/radio/radio.stories.svelte +1 -10
  123. package/dist/components/radio/radio.svelte +109 -109
  124. package/dist/components/radio/types.d.ts +98 -0
  125. package/dist/components/radio/types.js +2 -0
  126. package/dist/components/root/root.svelte +104 -121
  127. package/dist/components/scrollable/scrollable-root.svelte.d.ts +2 -2
  128. package/dist/components/scrollable/scrollable.stories.svelte +95 -105
  129. package/dist/components/sidebar/index.d.ts +2 -0
  130. package/dist/components/sidebar/index.js +2 -0
  131. package/dist/components/sidebar/motion.svelte.d.ts +11 -0
  132. package/dist/components/sidebar/motion.svelte.js +16 -0
  133. package/dist/components/sidebar/sidebar-content.svelte +3 -2
  134. package/dist/components/sidebar/sidebar-root.svelte +39 -41
  135. package/dist/components/sidebar/sidebar.stories.svelte +43 -54
  136. package/dist/components/sidebar/types.d.ts +3 -12
  137. package/dist/components/tabs/tab/bond.svelte.d.ts +4 -1
  138. package/dist/components/tabs/tab/bond.svelte.js +4 -1
  139. package/dist/components/tabs/tabs.stories.svelte +31 -34
  140. package/dist/components/textarea/atoms.d.ts +1 -0
  141. package/dist/components/textarea/atoms.js +1 -0
  142. package/dist/components/textarea/textarea-input.svelte +9 -6
  143. package/dist/components/textarea/textarea-root.svelte +9 -9
  144. package/dist/components/textarea/textarea-root.svelte.d.ts +2 -0
  145. package/dist/components/tooltip/tooltip-trigger.svelte +39 -37
  146. package/dist/components/tooltip/tooltip-trigger.svelte.d.ts +1 -0
  147. package/dist/components/tooltip/tooltip.stories.svelte +7 -10
  148. package/dist/components/tree/tree.stories.svelte +102 -94
  149. package/dist/context/preset.svelte.d.ts +3 -3
  150. package/dist/icons/icon-copy.svelte +6 -0
  151. package/dist/{components/radio/types.svelte.d.ts → icons/icon-copy.svelte.d.ts} +3 -3
  152. package/dist/utils/function.d.ts +2 -0
  153. package/dist/utils/function.js +6 -0
  154. package/dist/utils/markdown-to-llm.d.ts +28 -0
  155. package/dist/utils/markdown-to-llm.js +76 -0
  156. package/package.json +6 -10
  157. package/dist/actions/animation.svelte.d.ts +0 -6
  158. package/dist/actions/animation.svelte.js +0 -14
  159. package/dist/actions/clickout.svelte.d.ts +0 -2
  160. package/dist/actions/clickout.svelte.js +0 -15
  161. package/dist/actions/popover.svelte.d.ts +0 -19
  162. package/dist/actions/popover.svelte.js +0 -81
  163. package/dist/actions/portal.svelte.d.ts +0 -8
  164. package/dist/actions/portal.svelte.js +0 -32
  165. package/dist/attachments/gsap.svelte.d.ts +0 -2
  166. package/dist/attachments/gsap.svelte.js +0 -26
  167. package/dist/components/radio/types.svelte +0 -0
  168. package/llm/composition.md +0 -395
  169. package/llm/crafting.md +0 -838
  170. package/llm/motion.md +0 -970
  171. package/llm/philosophy.md +0 -23
  172. package/llm/preset-variant-integration.md +0 -516
  173. package/llm/preset.md +0 -383
  174. package/llm/styling.md +0 -216
  175. package/llm/usage.md +0 -46
  176. package/llm/variants.md +0 -1259
@@ -1,205 +1,205 @@
1
- <script lang="ts">
2
- import { animate } from 'motion';
3
- import { getYear, setYear } from 'date-fns';
4
- import { cn } from '../../utils';
5
- import { DatePickerBond } from './bond.svelte';
6
- import type { DatePickerYearsProps } from './types';
7
- import { HtmlAtom } from '../atom';
8
- import { Icon } from '../icon';
9
-
10
- const datePicker = DatePickerBond.get();
11
-
12
- const pivote = $derived(datePicker?.state.props.pivote ?? new Date());
13
-
14
- let pivoteYear = $derived(pivote.getFullYear());
15
-
16
- const currentYear = $derived(getYear(pivote));
17
-
18
- // Generate array of years to display (12 years: current ±5)
19
- const yearsGrid = $derived.by(() => {
20
- const years = [];
21
- const startYear = pivoteYear - 5;
22
- for (let i = 0; i < 12; i++) {
23
- years.push(startYear + i);
24
- }
25
- return years;
26
- });
27
-
28
- let {
29
- class: klass = '',
30
- preset = 'datepicker.years',
31
- children,
32
- ...restProps
33
- }: DatePickerYearsProps = $props();
34
-
35
- let scrollTimeout: NodeJS.Timeout | undefined = undefined;
36
-
37
- function enter(node: HTMLElement) {
38
- animate(
39
- node,
40
- {
41
- scale: [0.8, 1]
42
- },
43
- { duration: 100 / 1000, ease: 'circOut' }
44
- );
45
- return {
46
- duration: 100
47
- };
48
- }
49
-
50
- function exit(node: HTMLElement) {
51
- animate(
52
- node,
53
- {
54
- scale: 0.8
55
- },
56
- { duration: 100 / 1000, ease: 'circOut' }
57
- );
58
- return {
59
- duration: 100
60
- };
61
- }
62
-
63
- function handlePreviousYear() {
64
- pivoteYear = pivoteYear - 1;
65
- }
66
-
67
- function handleNextYear() {
68
- pivoteYear = pivoteYear + 1;
69
- }
70
-
71
- function handleYearSelect(year: number) {
72
- if (!datePicker?.state.props.pivote) return;
73
- const current = datePicker.state.props.pivote;
74
- datePicker.state.props.pivote = setYear(current, year);
75
-
76
- datePicker.state.closeYearsPicker();
77
- }
78
-
79
- function handleWheel(event: WheelEvent) {
80
- event.preventDefault();
81
-
82
- // Clear any existing timeout
83
- if (scrollTimeout) {
84
- clearTimeout(scrollTimeout);
85
- }
86
-
87
- // Debounce the scroll event to avoid rapid year changes
88
- scrollTimeout = setTimeout(() => {
89
- const direction = event.deltaY > 0 ? 1 : -1; // Positive = scroll down = next year
90
- pivoteYear = pivoteYear + direction;
91
- }, 50);
92
- }
93
- </script>
94
-
95
- {#if datePicker.state.isYearsPickerOpen}
96
- <HtmlAtom
97
- class={['absolute inset-0 z-2 flex flex-col gap-2 bg-inherit opacity-0', '$preset', klass]}
98
- enter={(node) => {
99
- animate(
100
- node,
101
- {
102
- opacity: [0, 1]
103
- },
104
- { duration: 100 / 1000, ease: 'anticipate' }
105
- );
106
- return {
107
- duration: 100
108
- };
109
- }}
110
- exit={(node) => {
111
- animate(
112
- node,
113
- {
114
- opacity: 0
115
- },
116
- { duration: 100 / 1000, ease: 'anticipate' }
117
- );
118
- return {
119
- duration: 100
120
- };
121
- }}
122
- onwheel={handleWheel}
123
- {preset}
124
- {...restProps}
125
- >
126
- <HtmlAtom class="flex flex-1 flex-col" {enter} {exit}>
127
- <!-- Navigation Bar -->
128
- <nav
129
- class="border-border text-foreground flex h-12 items-center justify-between gap-2 border-b px-2 py-2"
130
- >
131
- <!-- Previous Year Button -->
132
- <button
133
- type="button"
134
- class="hover:bg-foreground/10 active:bg-foreground/20 flex size-8 cursor-pointer items-center justify-center rounded-md transition-colors"
135
- onclick={handlePreviousYear}
136
- aria-label="Previous year"
137
- >
138
- <Icon class="size-5">
139
- <svg
140
- xmlns="http://www.w3.org/2000/svg"
141
- class="size-full"
142
- viewBox="0 0 24 24"
143
- fill="none"
144
- stroke="currentColor"
145
- stroke-width="2"
146
- stroke-linecap="round"
147
- stroke-linejoin="round"
148
- >
149
- <path d="M15 18l-6-6 6-6" />
150
- </svg>
151
- </Icon>
152
- </button>
153
-
154
- <!-- Year Display -->
155
- <div class="flex-1">
156
- <!-- {currentYear} -->
157
- </div>
158
-
159
- <!-- Next Year Button -->
160
- <button
161
- type="button"
162
- class="hover:bg-foreground/10 active:bg-foreground/20 flex size-8 cursor-pointer items-center justify-center rounded-md transition-colors"
163
- onclick={handleNextYear}
164
- aria-label="Next year"
165
- >
166
- <Icon class="size-5">
167
- <svg
168
- xmlns="http://www.w3.org/2000/svg"
169
- class="size-full"
170
- viewBox="0 0 24 24"
171
- fill="none"
172
- stroke="currentColor"
173
- stroke-width="2"
174
- stroke-linecap="round"
175
- stroke-linejoin="round"
176
- >
177
- <path d="M9 18l6-6-6-6" />
178
- </svg>
179
- </Icon>
180
- </button>
181
- </nav>
182
-
183
- <!-- Years Grid -->
184
- <div class="grid flex-1 grid-cols-4 gap-1 px-2 py-2">
185
- {#each yearsGrid as year}
186
- {@const isSelected = year === pivote.getFullYear()}
187
- {@const isCurrent = year === currentYear}
188
- <button
189
- type="button"
190
- class={cn(
191
- 'hover:bg-foreground/10 active:bg-foreground/20 rounded-md px-3 py-2 text-sm transition-colors',
192
- isSelected && 'bg-primary text-primary-foreground hover:bg-primary/90',
193
- isCurrent && !isSelected && 'ring-foreground/20 ring-1'
194
- )}
195
- onclick={() => handleYearSelect(year)}
196
- aria-label="Select year {year}"
197
- aria-current={isSelected ? 'date' : undefined}
198
- >
199
- {year}
200
- </button>
201
- {/each}
202
- </div>
203
- </HtmlAtom>
204
- </HtmlAtom>
205
- {/if}
1
+ <script lang="ts">
2
+ import { animate } from 'motion';
3
+ import { getYear, setYear } from 'date-fns';
4
+ import { cn } from '../../utils';
5
+ import { DatePickerBond } from './bond.svelte';
6
+ import type { DatePickerYearsProps } from './types';
7
+ import { HtmlAtom } from '../atom';
8
+ import { Icon } from '../icon';
9
+
10
+ const datePicker = DatePickerBond.get();
11
+
12
+ const pivote = $derived(datePicker?.state.props.pivote ?? new Date());
13
+
14
+ let pivoteYear = $derived(pivote.getFullYear());
15
+
16
+ const currentYear = $derived(getYear(pivote));
17
+
18
+ // Generate array of years to display (12 years: current ±5)
19
+ const yearsGrid = $derived.by(() => {
20
+ const years = [];
21
+ const startYear = pivoteYear - 5;
22
+ for (let i = 0; i < 12; i++) {
23
+ years.push(startYear + i);
24
+ }
25
+ return years;
26
+ });
27
+
28
+ let {
29
+ class: klass = '',
30
+ preset = 'datepicker.years',
31
+ children,
32
+ ...restProps
33
+ }: DatePickerYearsProps = $props();
34
+
35
+ let scrollTimeout: NodeJS.Timeout | undefined = undefined;
36
+
37
+ function enter(node: HTMLElement) {
38
+ animate(
39
+ node,
40
+ {
41
+ scale: [0.8, 1]
42
+ },
43
+ { duration: 100 / 1000, ease: 'circOut' }
44
+ );
45
+ return {
46
+ duration: 100
47
+ };
48
+ }
49
+
50
+ function exit(node: HTMLElement) {
51
+ animate(
52
+ node,
53
+ {
54
+ scale: 0.8
55
+ },
56
+ { duration: 100 / 1000, ease: 'circOut' }
57
+ );
58
+ return {
59
+ duration: 100
60
+ };
61
+ }
62
+
63
+ function handlePreviousYear() {
64
+ pivoteYear = pivoteYear - 1;
65
+ }
66
+
67
+ function handleNextYear() {
68
+ pivoteYear = pivoteYear + 1;
69
+ }
70
+
71
+ function handleYearSelect(year: number) {
72
+ if (!datePicker?.state.props.pivote) return;
73
+ const current = datePicker.state.props.pivote;
74
+ datePicker.state.props.pivote = setYear(current, year);
75
+
76
+ datePicker.state.closeYearsPicker();
77
+ }
78
+
79
+ function handleWheel(event: WheelEvent) {
80
+ event.preventDefault();
81
+
82
+ // Clear any existing timeout
83
+ if (scrollTimeout) {
84
+ clearTimeout(scrollTimeout);
85
+ }
86
+
87
+ // Debounce the scroll event to avoid rapid year changes
88
+ scrollTimeout = setTimeout(() => {
89
+ const direction = event.deltaY > 0 ? 1 : -1; // Positive = scroll down = next year
90
+ pivoteYear = pivoteYear + direction;
91
+ }, 50);
92
+ }
93
+ </script>
94
+
95
+ {#if datePicker.state.isYearsPickerOpen}
96
+ <HtmlAtom
97
+ class={['absolute inset-0 z-2 flex flex-col gap-2 bg-inherit opacity-0', '$preset', klass]}
98
+ enter={(node) => {
99
+ animate(
100
+ node,
101
+ {
102
+ opacity: [0, 1]
103
+ },
104
+ { duration: 100 / 1000, ease: 'anticipate' }
105
+ );
106
+ return {
107
+ duration: 100
108
+ };
109
+ }}
110
+ exit={(node) => {
111
+ animate(
112
+ node,
113
+ {
114
+ opacity: 0
115
+ },
116
+ { duration: 100 / 1000, ease: 'anticipate' }
117
+ );
118
+ return {
119
+ duration: 100
120
+ };
121
+ }}
122
+ onwheel={handleWheel}
123
+ {preset}
124
+ {...restProps}
125
+ >
126
+ <HtmlAtom class="flex flex-1 flex-col" {enter} {exit}>
127
+ <!-- Navigation Bar -->
128
+ <nav
129
+ class="border-border text-foreground flex h-12 items-center justify-between gap-2 border-b px-2 py-2"
130
+ >
131
+ <!-- Previous Year Button -->
132
+ <button
133
+ type="button"
134
+ class="hover:bg-foreground/10 active:bg-foreground/20 flex size-8 cursor-pointer items-center justify-center rounded-md transition-colors"
135
+ onclick={handlePreviousYear}
136
+ aria-label="Previous year"
137
+ >
138
+ <Icon class="size-5">
139
+ <svg
140
+ xmlns="http://www.w3.org/2000/svg"
141
+ class="size-full"
142
+ viewBox="0 0 24 24"
143
+ fill="none"
144
+ stroke="currentColor"
145
+ stroke-width="2"
146
+ stroke-linecap="round"
147
+ stroke-linejoin="round"
148
+ >
149
+ <path d="M15 18l-6-6 6-6" />
150
+ </svg>
151
+ </Icon>
152
+ </button>
153
+
154
+ <!-- Year Display -->
155
+ <div class="flex-1">
156
+ <!-- {currentYear} -->
157
+ </div>
158
+
159
+ <!-- Next Year Button -->
160
+ <button
161
+ type="button"
162
+ class="hover:bg-foreground/10 active:bg-foreground/20 flex size-8 cursor-pointer items-center justify-center rounded-md transition-colors"
163
+ onclick={handleNextYear}
164
+ aria-label="Next year"
165
+ >
166
+ <Icon class="size-5">
167
+ <svg
168
+ xmlns="http://www.w3.org/2000/svg"
169
+ class="size-full"
170
+ viewBox="0 0 24 24"
171
+ fill="none"
172
+ stroke="currentColor"
173
+ stroke-width="2"
174
+ stroke-linecap="round"
175
+ stroke-linejoin="round"
176
+ >
177
+ <path d="M9 18l6-6-6-6" />
178
+ </svg>
179
+ </Icon>
180
+ </button>
181
+ </nav>
182
+
183
+ <!-- Years Grid -->
184
+ <div class="grid flex-1 grid-cols-4 gap-1 px-2 py-2">
185
+ {#each yearsGrid as year}
186
+ {@const isSelected = year === pivote.getFullYear()}
187
+ {@const isCurrent = year === currentYear}
188
+ <button
189
+ type="button"
190
+ class={cn(
191
+ 'hover:bg-foreground/10 active:bg-foreground/20 rounded-md px-3 py-2 text-sm transition-colors',
192
+ isSelected && 'bg-primary text-primary-foreground hover:bg-primary/90',
193
+ isCurrent && !isSelected && 'ring-foreground/20 ring-1'
194
+ )}
195
+ onclick={() => handleYearSelect(year)}
196
+ aria-label="Select year {year}"
197
+ aria-current={isSelected ? 'date' : undefined}
198
+ >
199
+ {year}
200
+ </button>
201
+ {/each}
202
+ </div>
203
+ </HtmlAtom>
204
+ </HtmlAtom>
205
+ {/if}
@@ -1,42 +1,35 @@
1
- <script module>
2
- import { defineMeta } from '@storybook/addon-svelte-csf';
3
-
4
- const { Story } = defineMeta({
5
- title: 'Atoms/Date Picker'
6
- });
7
- </script>
8
-
9
- <script lang="ts">
10
- import { Root } from '../root';
11
- import { DatePicker as ADatePicker } from '.';
12
- import { Button } from '../button';
13
- import { addDays, subDays } from 'date-fns';
14
-
15
- let value: Date | undefined = $state(undefined);
16
-
17
- let min = $state(subDays(new Date(), 5));
18
- let max = $state(addDays(new Date(), 15));
19
- </script>
20
-
21
- <Story name="Date Picker">
22
- {#snippet children({ args })}
23
- <Root>
24
- {#snippet children({})}
25
- <div class="flex h-fit items-center justify-center">
26
- <ADatePicker.Root bind:value {min} {max}>
27
- <ADatePicker.Trigger base={Button} class="w-sm gap-4">
28
- {#if value}
29
- <div>{value.toDateString()}</div>
30
- {:else}
31
- <div>Open Date Picker</div>
32
- {/if}
33
-
34
- <ADatePicker.Indicator class="ml-auto" />
35
- </ADatePicker.Trigger>
36
- <ADatePicker.Calendar />
37
- </ADatePicker.Root>
38
- </div>
39
- {/snippet}
40
- </Root>
41
- {/snippet}
42
- </Story>
1
+ <script module>
2
+ import { defineMeta } from '@storybook/addon-svelte-csf';
3
+
4
+ const { Story } = defineMeta({
5
+ title: 'Atoms/Date Picker'
6
+ });
7
+ </script>
8
+
9
+ <script lang="ts">
10
+ import { DatePicker as ADatePicker } from '.';
11
+ import { Button } from '../button';
12
+ import { addDays, subDays } from 'date-fns';
13
+
14
+ let value: Date | undefined = $state(undefined);
15
+
16
+ let min = $state(subDays(new Date(), 5));
17
+ let max = $state(addDays(new Date(), 15));
18
+ </script>
19
+
20
+ <Story name="Date Picker">
21
+ {#snippet children({ args })}
22
+ <ADatePicker.Root bind:value {min} {max}>
23
+ <ADatePicker.Trigger base={Button} class="w-sm gap-4">
24
+ {#if value}
25
+ <div>{value.toDateString()}</div>
26
+ {:else}
27
+ <div>Open Date Picker</div>
28
+ {/if}
29
+
30
+ <ADatePicker.Indicator class="ml-auto" />
31
+ </ADatePicker.Trigger>
32
+ <ADatePicker.Calendar />
33
+ </ADatePicker.Root>
34
+ {/snippet}
35
+ </Story>
@@ -16,26 +16,33 @@ export declare class DialogBond<State extends DialogBondState<DialogBondProps> =
16
16
  static CONTEXT_KEY: string;
17
17
  constructor(s: State);
18
18
  share(): this;
19
- root(props: Record<string, unknown>): {
19
+ root(): {
20
+ [x: symbol]: (node: HTMLDialogElement) => void;
20
21
  id: string;
21
22
  role: string;
22
23
  'aria-modal': boolean;
23
24
  'aria-labelledby': string;
24
25
  'aria-describedby': string;
25
- 'aria-opened': boolean;
26
- 'aria-disabled': boolean;
26
+ inert: string | undefined;
27
27
  'data-kind': string;
28
+ 'data-open': boolean;
29
+ onclick: (ev: MouseEvent) => void;
30
+ onkeydown: (ev: KeyboardEvent) => void;
28
31
  };
29
32
  content(props: Record<string, unknown>): {
30
33
  id: string;
34
+ role: string;
31
35
  'data-kind': string;
32
36
  };
33
37
  header(props: Record<string, unknown>): {
34
38
  id: string;
39
+ role: string;
35
40
  'data-kind': string;
36
41
  };
37
42
  title(props: Record<string, unknown>): {
38
43
  id: string;
44
+ role: string;
45
+ 'aria-level': number;
39
46
  'data-kind': string;
40
47
  };
41
48
  description(props: Record<string, unknown>): {
@@ -44,10 +51,13 @@ export declare class DialogBond<State extends DialogBondState<DialogBondProps> =
44
51
  };
45
52
  body(props: Record<string, unknown>): {
46
53
  id: string;
54
+ role: string;
55
+ 'aria-live': string;
47
56
  'data-kind': string;
48
57
  };
49
58
  footer(props: Record<string, unknown>): {
50
59
  id: string;
60
+ role: string;
51
61
  'data-kind': string;
52
62
  };
53
63
  static get(): DialogBond | undefined;
@@ -19,24 +19,78 @@ export class DialogBond extends Bond {
19
19
  share() {
20
20
  return DialogBond.set(this);
21
21
  }
22
- root(props) {
22
+ root() {
23
23
  const id = getElementId(this.id, DIALOG_ELEMENTS_KIND.root);
24
24
  const titleId = getElementId(this.id, DIALOG_ELEMENTS_KIND.title);
25
25
  const descriptionId = getElementId(this.id, DIALOG_ELEMENTS_KIND.description);
26
- const idOpened = this.state.props.open ?? false;
26
+ const isOpen = this.state.props.open ?? false;
27
27
  const isDisabled = this.state.props.disabled ?? false;
28
+ // Focus trap handler
29
+ const focusTrap = (ev) => {
30
+ const node = ev.currentTarget;
31
+ if (ev.key === 'Tab') {
32
+ const focusableElements = node.querySelectorAll('a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])');
33
+ const firstElement = focusableElements[0];
34
+ const lastElement = focusableElements[focusableElements.length - 1];
35
+ if (focusableElements.length === 0)
36
+ return;
37
+ if (ev.shiftKey && document.activeElement === firstElement) {
38
+ ev.preventDefault();
39
+ lastElement?.focus();
40
+ }
41
+ else if (!ev.shiftKey && document.activeElement === lastElement) {
42
+ ev.preventDefault();
43
+ firstElement?.focus();
44
+ }
45
+ }
46
+ };
47
+ let previousActiveElement = null;
28
48
  return {
29
49
  id,
30
50
  role: 'dialog',
31
51
  'aria-modal': true,
32
52
  'aria-labelledby': titleId,
33
53
  'aria-describedby': descriptionId,
34
- 'aria-opened': idOpened,
35
- 'aria-disabled': isDisabled,
54
+ inert: !isOpen ? '' : undefined,
36
55
  'data-kind': DIALOG_ELEMENTS_KIND.root,
37
- ...props,
56
+ 'data-open': isOpen,
57
+ onclick: (ev) => {
58
+ // Close on backdrop click (clicking outside content)
59
+ if (ev.target === ev.currentTarget && !isDisabled) {
60
+ this.state.close();
61
+ }
62
+ },
63
+ onkeydown: (ev) => {
64
+ focusTrap(ev);
65
+ // Close on Escape key
66
+ if (ev.key === 'Escape' && !isDisabled) {
67
+ ev.preventDefault();
68
+ this.state.close();
69
+ }
70
+ },
38
71
  [createAttachmentKey()]: (node) => {
39
72
  this.elements.root = node;
73
+ if (this.state.props.open) {
74
+ // Store current focus
75
+ previousActiveElement = document.activeElement;
76
+ // Focus first focusable element or dialog itself
77
+ setTimeout(() => {
78
+ const firstFocusable = node.querySelector('a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])');
79
+ if (firstFocusable) {
80
+ firstFocusable.focus();
81
+ }
82
+ else {
83
+ node.focus();
84
+ }
85
+ }, 0);
86
+ }
87
+ else {
88
+ console.log('restoring focus to', previousActiveElement);
89
+ // Restore focus to previous element
90
+ if (previousActiveElement instanceof HTMLElement) {
91
+ previousActiveElement.focus();
92
+ }
93
+ }
40
94
  }
41
95
  };
42
96
  }
@@ -44,6 +98,7 @@ export class DialogBond extends Bond {
44
98
  const id = getElementId(this.id, DIALOG_ELEMENTS_KIND.content);
45
99
  return {
46
100
  id,
101
+ role: 'document',
47
102
  'data-kind': DIALOG_ELEMENTS_KIND.content,
48
103
  ...props,
49
104
  [createAttachmentKey()]: (node) => {
@@ -55,6 +110,7 @@ export class DialogBond extends Bond {
55
110
  const id = getElementId(this.id, DIALOG_ELEMENTS_KIND.header);
56
111
  return {
57
112
  id,
113
+ role: 'banner',
58
114
  'data-kind': DIALOG_ELEMENTS_KIND.header,
59
115
  ...props,
60
116
  [createAttachmentKey()]: (node) => {
@@ -66,6 +122,8 @@ export class DialogBond extends Bond {
66
122
  const id = getElementId(this.id, DIALOG_ELEMENTS_KIND.title);
67
123
  return {
68
124
  id,
125
+ role: 'heading',
126
+ 'aria-level': 2,
69
127
  'data-kind': DIALOG_ELEMENTS_KIND.title,
70
128
  ...props,
71
129
  [createAttachmentKey()]: (node) => {
@@ -88,6 +146,8 @@ export class DialogBond extends Bond {
88
146
  const id = getElementId(this.id, DIALOG_ELEMENTS_KIND.body);
89
147
  return {
90
148
  id,
149
+ role: 'region',
150
+ 'aria-live': 'polite',
91
151
  'data-kind': DIALOG_ELEMENTS_KIND.body,
92
152
  ...props,
93
153
  [createAttachmentKey()]: (node) => {
@@ -99,6 +159,7 @@ export class DialogBond extends Bond {
99
159
  const id = getElementId(this.id, DIALOG_ELEMENTS_KIND.footer);
100
160
  return {
101
161
  id,
162
+ role: 'contentinfo',
102
163
  'data-kind': DIALOG_ELEMENTS_KIND.footer,
103
164
  ...props,
104
165
  [createAttachmentKey()]: (node) => {