@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.
- package/README.md +3 -2
- package/dist/attachments/clickout.svelte.d.ts +1 -1
- package/dist/attachments/clickout.svelte.js +2 -2
- package/dist/components/accordion/accordion-root.svelte +65 -61
- package/dist/components/accordion/accordion-root.svelte.d.ts +1 -1
- package/dist/components/accordion/accordion.stories.svelte +70 -134
- package/dist/components/accordion/item/accordion-item-body.svelte +44 -42
- package/dist/components/accordion/item/accordion-item-header.svelte +51 -50
- package/dist/components/accordion/item/accordion-item-indicator.svelte +51 -50
- package/dist/components/accordion/item/accordion-item-root.svelte +66 -65
- package/dist/components/accordion/item/bond.svelte.d.ts +2 -0
- package/dist/components/accordion/item/index.d.ts +3 -0
- package/dist/components/accordion/item/index.js +3 -0
- package/dist/components/accordion/item/motion.svelte.d.ts +15 -0
- package/dist/components/accordion/item/motion.svelte.js +30 -0
- package/dist/components/accordion/item/types.d.ts +7 -24
- package/dist/components/alert/alert-close-button.svelte +66 -70
- package/dist/components/alert/alert-description.svelte +42 -42
- package/dist/components/alert/alert-description.svelte.d.ts +3 -6
- package/dist/components/alert/alert-root.svelte +68 -103
- package/dist/components/alert/alert-root.svelte.d.ts +2 -2
- package/dist/components/alert/alert.stories.svelte +10 -11
- package/dist/components/alert/bond.svelte.d.ts +0 -13
- package/dist/components/alert/bond.svelte.js +0 -32
- package/dist/components/alert/types.d.ts +8 -32
- package/dist/components/atom/html-atom.svelte +261 -207
- package/dist/components/avatar/avatar.stories.svelte +8 -13
- package/dist/components/badge/badge.stories.svelte +1 -6
- package/dist/components/badge/badge.svelte +1 -1
- package/dist/components/breadcrumb/breadcrumb.stories.svelte +16 -21
- package/dist/components/button/button.stories.svelte +1 -34
- package/dist/components/calendar/calendar-day.svelte +9 -4
- package/dist/components/calendar/calendar-header.svelte +29 -29
- package/dist/components/calendar/calendar-root.svelte +206 -206
- package/dist/components/calendar/calendar.stories.svelte +26 -31
- package/dist/components/card/card-body.svelte +1 -1
- package/dist/components/card/card-footer.svelte +1 -1
- package/dist/components/card/card-root.svelte +1 -1
- package/dist/components/card/card.stories.svelte +92 -104
- package/dist/components/checkbox/checkbox.stories.svelte +4 -9
- package/dist/components/checkbox/checkbox.svelte +159 -157
- package/dist/components/collapsible/collapsible.stories.svelte +2 -3
- package/dist/components/combobox/atoms.d.ts +1 -1
- package/dist/components/combobox/atoms.js +1 -1
- package/dist/components/combobox/combobox-root.svelte +1 -1
- package/dist/components/combobox/compobox.stories.svelte +19 -22
- package/dist/components/combobox/index.d.ts +1 -0
- package/dist/components/container/container.stories.svelte +8 -11
- package/dist/components/container/container.svelte.d.ts +1 -1
- package/dist/components/datagrid/datagrid-root.svelte +59 -59
- package/dist/components/datagrid/datagrid.css +5 -5
- package/dist/components/datagrid/datagrid.stories.svelte +47 -50
- package/dist/components/datagrid/tr/bond.svelte.d.ts +4 -2
- package/dist/components/datagrid/tr/bond.svelte.js +9 -7
- package/dist/components/datagrid/tr/datagrid-tr.svelte +90 -88
- package/dist/components/date-picker/date-picker-calendar.svelte +2 -2
- package/dist/components/date-picker/date-picker-header.svelte +100 -100
- package/dist/components/date-picker/date-picker-months.svelte +142 -142
- package/dist/components/date-picker/date-picker-root.svelte +95 -95
- package/dist/components/date-picker/date-picker-years.svelte +205 -205
- package/dist/components/date-picker/date-picker.stories.svelte +35 -42
- package/dist/components/dialog/bond.svelte.d.ts +13 -3
- package/dist/components/dialog/bond.svelte.js +66 -5
- package/dist/components/dialog/dialog-content.svelte +2 -20
- package/dist/components/dialog/dialog-root.svelte +91 -110
- package/dist/components/dialog/dialog.stories.svelte +34 -37
- package/dist/components/dialog/motion.svelte.d.ts +13 -0
- package/dist/components/dialog/motion.svelte.js +44 -0
- package/dist/components/drawer/attachments.svelte.d.ts +1 -1
- package/dist/components/drawer/attachments.svelte.js +7 -10
- package/dist/components/drawer/bond.svelte.d.ts +24 -5
- package/dist/components/drawer/bond.svelte.js +77 -11
- package/dist/components/drawer/drawer-content.svelte +49 -42
- package/dist/components/drawer/drawer.stories.svelte +144 -224
- package/dist/components/drawer/index.d.ts +2 -0
- package/dist/components/drawer/index.js +2 -0
- package/dist/components/drawer/motion.d.ts +15 -0
- package/dist/components/drawer/motion.js +28 -0
- package/dist/components/dropdown/atoms.d.ts +1 -1
- package/dist/components/dropdown/atoms.js +1 -1
- package/dist/components/dropdown/bond.svelte.d.ts +5 -1
- package/dist/components/dropdown/dropdown-root.svelte +1 -1
- package/dist/components/dropdown/dropdown.stories.svelte +38 -41
- package/dist/components/dropdown/index.d.ts +1 -0
- package/dist/components/form/form.stories.svelte +58 -61
- package/dist/components/image/image.stories.svelte +9 -12
- package/dist/components/input/input.stories.svelte +11 -14
- package/dist/components/label/label.stories.svelte +1 -12
- package/dist/components/label/label.stories.svelte.d.ts +24 -4
- package/dist/components/lazy/lazy.stories.svelte +28 -35
- package/dist/components/lazy/lazy.svelte +28 -28
- package/dist/components/link/link.stories.svelte +1 -12
- package/dist/components/link/link.stories.svelte.d.ts +24 -4
- package/dist/components/list/list-item.svelte +20 -20
- package/dist/components/menu/atoms.d.ts +1 -0
- package/dist/components/menu/atoms.js +1 -0
- package/dist/components/menu/index.d.ts +2 -1
- package/dist/components/menu/index.js +1 -1
- package/dist/components/menu/menu-item.svelte +69 -51
- package/dist/components/menu/menu-item.svelte.d.ts +1 -0
- package/dist/components/menu/menu-list.svelte +40 -40
- package/dist/components/menu/menu.stories.svelte +9 -12
- package/dist/components/popover/bond.svelte.d.ts +20 -7
- package/dist/components/popover/bond.svelte.js +104 -45
- package/dist/components/popover/motion.d.ts +6 -0
- package/dist/components/popover/motion.js +56 -0
- package/dist/components/popover/popover-arrow.svelte +4 -4
- package/dist/components/popover/popover-content.svelte +137 -178
- package/dist/components/popover/popover-indicator.svelte +2 -1
- package/dist/components/popover/popover-root.svelte +1 -1
- package/dist/components/popover/popover.stories.svelte +49 -52
- package/dist/components/popover/types.d.ts +9 -7
- package/dist/components/portal/active-portal.svelte +29 -22
- package/dist/components/portal/active-portal.svelte.d.ts +2 -9
- package/dist/components/portal/portal-root.svelte +76 -83
- package/dist/components/portal/portal-root.svelte.d.ts +4 -6
- package/dist/components/portal/teleport.svelte +49 -50
- package/dist/components/portal/teleport.svelte.d.ts +3 -4
- package/dist/components/qr-code/qr-code.stories.svelte +18 -27
- package/dist/components/qr-code/qr-code.svelte +75 -75
- package/dist/components/radio/radio-group.stories.svelte +21 -30
- package/dist/components/radio/radio.stories.svelte +1 -10
- package/dist/components/radio/radio.svelte +109 -109
- package/dist/components/radio/types.d.ts +98 -0
- package/dist/components/radio/types.js +2 -0
- package/dist/components/root/root.svelte +104 -121
- package/dist/components/scrollable/scrollable-root.svelte.d.ts +2 -2
- package/dist/components/scrollable/scrollable.stories.svelte +95 -105
- package/dist/components/sidebar/index.d.ts +2 -0
- package/dist/components/sidebar/index.js +2 -0
- package/dist/components/sidebar/motion.svelte.d.ts +11 -0
- package/dist/components/sidebar/motion.svelte.js +16 -0
- package/dist/components/sidebar/sidebar-content.svelte +3 -2
- package/dist/components/sidebar/sidebar-root.svelte +39 -41
- package/dist/components/sidebar/sidebar.stories.svelte +43 -54
- package/dist/components/sidebar/types.d.ts +3 -12
- package/dist/components/tabs/tab/bond.svelte.d.ts +4 -1
- package/dist/components/tabs/tab/bond.svelte.js +4 -1
- package/dist/components/tabs/tabs.stories.svelte +31 -34
- package/dist/components/textarea/atoms.d.ts +1 -0
- package/dist/components/textarea/atoms.js +1 -0
- package/dist/components/textarea/textarea-input.svelte +9 -6
- package/dist/components/textarea/textarea-root.svelte +9 -9
- package/dist/components/textarea/textarea-root.svelte.d.ts +2 -0
- package/dist/components/tooltip/tooltip-trigger.svelte +39 -37
- package/dist/components/tooltip/tooltip-trigger.svelte.d.ts +1 -0
- package/dist/components/tooltip/tooltip.stories.svelte +7 -10
- package/dist/components/tree/tree.stories.svelte +102 -94
- package/dist/context/preset.svelte.d.ts +3 -3
- package/dist/icons/icon-copy.svelte +6 -0
- package/dist/{components/radio/types.svelte.d.ts → icons/icon-copy.svelte.d.ts} +3 -3
- package/dist/utils/function.d.ts +2 -0
- package/dist/utils/function.js +6 -0
- package/dist/utils/markdown-to-llm.d.ts +28 -0
- package/dist/utils/markdown-to-llm.js +76 -0
- package/package.json +6 -10
- package/dist/actions/animation.svelte.d.ts +0 -6
- package/dist/actions/animation.svelte.js +0 -14
- package/dist/actions/clickout.svelte.d.ts +0 -2
- package/dist/actions/clickout.svelte.js +0 -15
- package/dist/actions/popover.svelte.d.ts +0 -19
- package/dist/actions/popover.svelte.js +0 -81
- package/dist/actions/portal.svelte.d.ts +0 -8
- package/dist/actions/portal.svelte.js +0 -32
- package/dist/attachments/gsap.svelte.d.ts +0 -2
- package/dist/attachments/gsap.svelte.js +0 -26
- package/dist/components/radio/types.svelte +0 -0
- package/llm/composition.md +0 -395
- package/llm/crafting.md +0 -838
- package/llm/motion.md +0 -970
- package/llm/philosophy.md +0 -23
- package/llm/preset-variant-integration.md +0 -516
- package/llm/preset.md +0 -383
- package/llm/styling.md +0 -216
- package/llm/usage.md +0 -46
- 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 {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
let
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
-
'
|
|
35
|
-
'aria-disabled': isDisabled,
|
|
54
|
+
inert: !isOpen ? '' : undefined,
|
|
36
55
|
'data-kind': DIALOG_ELEMENTS_KIND.root,
|
|
37
|
-
|
|
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) => {
|