@svelte-atoms/core 1.0.0-alpha.30 → 1.0.0-alpha.32

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 (221) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +176 -739
  3. package/dist/attachments/index.d.ts +1 -0
  4. package/dist/attachments/index.js +1 -0
  5. package/dist/components/accordion/accordion-root.svelte +65 -61
  6. package/dist/components/accordion/accordion.stories.svelte +70 -145
  7. package/dist/components/accordion/item/accordion-item-body.svelte +6 -4
  8. package/dist/components/accordion/item/accordion-item-header.svelte +2 -1
  9. package/dist/components/accordion/item/accordion-item-indicator.svelte +2 -1
  10. package/dist/components/accordion/item/accordion-item-root.svelte +2 -1
  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 +32 -36
  18. package/dist/components/alert/alert-description.svelte +1 -1
  19. package/dist/components/alert/alert-description.svelte.d.ts +3 -6
  20. package/dist/components/alert/alert-root.svelte +3 -38
  21. package/dist/components/alert/alert-root.svelte.d.ts +2 -2
  22. package/dist/components/alert/alert.stories.svelte +400 -400
  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 +93 -261
  27. package/dist/components/atom/types.d.ts +3 -2
  28. package/dist/components/atom/utils.d.ts +37 -0
  29. package/dist/components/atom/utils.js +208 -0
  30. package/dist/components/avatar/avatar.stories.svelte +22 -22
  31. package/dist/components/badge/badge.stories.svelte +12 -12
  32. package/dist/components/badge/badge.svelte +19 -19
  33. package/dist/components/breadcrumb/breadcrumb-item.svelte +1 -1
  34. package/dist/components/breadcrumb/breadcrumb-separator.svelte +5 -1
  35. package/dist/components/breadcrumb/breadcrumb.stories.svelte +16 -16
  36. package/dist/components/button/button.stories.svelte +27 -27
  37. package/dist/components/calendar/calendar-day.svelte +9 -4
  38. package/dist/components/calendar/calendar.stories.svelte +26 -26
  39. package/dist/components/card/card-body.svelte +39 -39
  40. package/dist/components/card/card-footer.svelte +41 -41
  41. package/dist/components/card/card-root.svelte +91 -91
  42. package/dist/components/card/card.stories.svelte +133 -133
  43. package/dist/components/checkbox/checkbox.stories.svelte +22 -22
  44. package/dist/components/checkbox/checkbox.svelte +159 -155
  45. package/dist/components/collapsible/bond.svelte.js +2 -1
  46. package/dist/components/collapsible/collapsible-body.svelte +3 -2
  47. package/dist/components/collapsible/collapsible.stories.svelte +172 -172
  48. package/dist/components/collapsible/motion.svelte.d.ts +6 -0
  49. package/dist/components/collapsible/motion.svelte.js +15 -0
  50. package/dist/components/combobox/atoms.d.ts +3 -3
  51. package/dist/components/combobox/atoms.js +3 -3
  52. package/dist/components/combobox/bond.svelte.d.ts +6 -6
  53. package/dist/components/combobox/bond.svelte.js +3 -26
  54. package/dist/components/combobox/combobox-control.svelte +52 -52
  55. package/dist/components/combobox/{compobox-item.svelte → combobox-item.svelte} +62 -68
  56. package/dist/components/combobox/combobox-item.svelte.d.ts +12 -0
  57. package/dist/components/combobox/combobox-root.svelte +65 -65
  58. package/dist/components/combobox/combobox.stories.svelte +50 -0
  59. package/dist/components/combobox/combobox.stories.svelte.d.ts +3 -0
  60. package/dist/components/combobox/index.d.ts +1 -0
  61. package/dist/components/container/container.stories.svelte +20 -20
  62. package/dist/components/container/container.svelte.d.ts +1 -1
  63. package/dist/components/datagrid/datagrid.stories.svelte +72 -72
  64. package/dist/components/datagrid/tr/bond.svelte.d.ts +4 -2
  65. package/dist/components/datagrid/tr/bond.svelte.js +9 -7
  66. package/dist/components/datagrid/tr/datagrid-tr.svelte +9 -7
  67. package/dist/components/date-picker/bond.svelte.d.ts +15 -5
  68. package/dist/components/date-picker/bond.svelte.js +5 -11
  69. package/dist/components/date-picker/date-picker-calendar.svelte +2 -2
  70. package/dist/components/date-picker/date-picker-root.svelte +95 -95
  71. package/dist/components/date-picker/date-picker.stories.svelte +35 -35
  72. package/dist/components/dialog/bond.svelte.d.ts +13 -3
  73. package/dist/components/dialog/bond.svelte.js +52 -6
  74. package/dist/components/dialog/dialog-content.svelte +2 -20
  75. package/dist/components/dialog/dialog-root.svelte +3 -22
  76. package/dist/components/dialog/dialog.stories.svelte +64 -64
  77. package/dist/components/dialog/motion.svelte.d.ts +13 -0
  78. package/dist/components/dialog/motion.svelte.js +44 -0
  79. package/dist/components/drawer/attachments.svelte.d.ts +1 -1
  80. package/dist/components/drawer/attachments.svelte.js +1 -3
  81. package/dist/components/drawer/bond.svelte.d.ts +30 -9
  82. package/dist/components/drawer/bond.svelte.js +80 -24
  83. package/dist/components/drawer/drawer-content.svelte +49 -57
  84. package/dist/components/drawer/drawer-root.svelte +5 -4
  85. package/dist/components/drawer/drawer.stories.svelte +141 -212
  86. package/dist/components/drawer/index.d.ts +2 -0
  87. package/dist/components/drawer/index.js +2 -0
  88. package/dist/components/drawer/motion.d.ts +15 -0
  89. package/dist/components/drawer/motion.js +28 -0
  90. package/dist/components/dropdown/atoms.d.ts +1 -1
  91. package/dist/components/dropdown/atoms.js +1 -1
  92. package/dist/components/dropdown/bond.svelte.d.ts +22 -19
  93. package/dist/components/dropdown/bond.svelte.js +29 -53
  94. package/dist/components/dropdown/dropdown-root.svelte +7 -1
  95. package/dist/components/dropdown/dropdown-values.svelte +17 -17
  96. package/dist/components/dropdown/dropdown-values.svelte.d.ts +1 -2
  97. package/dist/components/dropdown/dropdown.stories.svelte +13 -10
  98. package/dist/components/dropdown/index.d.ts +2 -0
  99. package/dist/components/dropdown/index.js +1 -0
  100. package/dist/components/dropdown/item/attachments.svelte.d.ts +2 -2
  101. package/dist/components/dropdown/item/attachments.svelte.js +2 -2
  102. package/dist/components/dropdown/item/controller.svelte.d.ts +34 -0
  103. package/dist/components/dropdown/item/controller.svelte.js +82 -0
  104. package/dist/components/dropdown/item/dropdown-item.svelte +109 -102
  105. package/dist/components/dropdown/item/dropdown-item.svelte.d.ts +13 -28
  106. package/dist/components/dropdown/item/index.d.ts +3 -0
  107. package/dist/components/dropdown/item/index.js +3 -0
  108. package/dist/components/dropdown/item/types.d.ts +29 -0
  109. package/dist/components/dropdown/item/types.js +1 -0
  110. package/dist/components/form/form.stories.svelte +96 -96
  111. package/dist/components/image/image.stories.svelte +20 -20
  112. package/dist/components/input/input.stories.svelte +35 -35
  113. package/dist/components/label/label.stories.svelte +15 -15
  114. package/dist/components/lazy/lazy.stories.svelte +28 -28
  115. package/dist/components/link/link.stories.svelte +15 -15
  116. package/dist/components/list/list-item.svelte +2 -2
  117. package/dist/components/menu/atoms.d.ts +9 -3
  118. package/dist/components/menu/atoms.js +9 -3
  119. package/dist/components/menu/bond.svelte.d.ts +54 -0
  120. package/dist/components/menu/bond.svelte.js +132 -0
  121. package/dist/components/menu/index.d.ts +3 -1
  122. package/dist/components/menu/index.js +2 -1
  123. package/dist/components/menu/item/controller.svelte.d.ts +26 -0
  124. package/dist/components/menu/item/controller.svelte.js +69 -0
  125. package/dist/components/menu/item/index.d.ts +2 -0
  126. package/dist/components/menu/item/index.js +2 -0
  127. package/dist/components/menu/item/menu-item.svelte +103 -0
  128. package/dist/components/menu/item/menu-item.svelte.d.ts +31 -0
  129. package/dist/components/menu/item/types.d.ts +62 -0
  130. package/dist/components/menu/item/types.js +1 -0
  131. package/dist/components/menu/{menu-list.svelte → menu-content.svelte} +40 -40
  132. package/dist/components/menu/{menu-list.svelte.d.ts → menu-content.svelte.d.ts} +3 -3
  133. package/dist/components/menu/menu-root.svelte +15 -0
  134. package/dist/components/menu/menu-root.svelte.d.ts +8 -0
  135. package/dist/components/menu/menu.stories.svelte +5 -5
  136. package/dist/components/menu/types.d.ts +0 -7
  137. package/dist/components/popover/bond.svelte.d.ts +18 -8
  138. package/dist/components/popover/bond.svelte.js +76 -40
  139. package/dist/components/popover/motion.d.ts +6 -0
  140. package/dist/components/popover/motion.js +56 -0
  141. package/dist/components/popover/popover-arrow.svelte +111 -111
  142. package/dist/components/popover/popover-content.svelte +137 -175
  143. package/dist/components/popover/popover-indicator.svelte +44 -44
  144. package/dist/components/popover/popover-root.svelte +48 -48
  145. package/dist/components/popover/popover.stories.svelte +37 -49
  146. package/dist/components/popover/types.d.ts +9 -7
  147. package/dist/components/portal/active-portal.svelte +12 -5
  148. package/dist/components/portal/active-portal.svelte.d.ts +2 -9
  149. package/dist/components/portal/portal-root.svelte +1 -8
  150. package/dist/components/portal/portal-root.svelte.d.ts +4 -6
  151. package/dist/components/portal/teleport.svelte +1 -2
  152. package/dist/components/portal/teleport.svelte.d.ts +3 -4
  153. package/dist/components/qr-code/qr-code.stories.svelte +18 -18
  154. package/dist/components/radio/radio-group.stories.svelte +41 -41
  155. package/dist/components/radio/radio.stories.svelte +17 -17
  156. package/dist/components/radio/radio.svelte +109 -109
  157. package/dist/components/radio/types.d.ts +98 -0
  158. package/dist/components/radio/types.js +2 -0
  159. package/dist/components/root/index.d.ts +1 -0
  160. package/dist/components/root/index.js +1 -0
  161. package/dist/components/root/l0-portal.svelte +8 -0
  162. package/dist/components/{radio/types.svelte.d.ts → root/l0-portal.svelte.d.ts} +3 -3
  163. package/dist/components/root/l1-portal.svelte +7 -0
  164. package/dist/components/root/l1-portal.svelte.d.ts +26 -0
  165. package/dist/components/root/root.css +119 -119
  166. package/dist/components/root/root.svelte +26 -44
  167. package/dist/components/root/root.svelte.d.ts +2 -6
  168. package/dist/components/root/toasts-portal.svelte +7 -0
  169. package/dist/components/root/toasts-portal.svelte.d.ts +26 -0
  170. package/dist/components/root/types.d.ts +17 -0
  171. package/dist/components/scrollable/scrollable-root.svelte.d.ts +2 -2
  172. package/dist/components/scrollable/scrollable.stories.svelte +116 -116
  173. package/dist/components/sidebar/index.d.ts +2 -0
  174. package/dist/components/sidebar/index.js +2 -0
  175. package/dist/components/sidebar/motion.svelte.d.ts +11 -0
  176. package/dist/components/sidebar/motion.svelte.js +16 -0
  177. package/dist/components/sidebar/sidebar-content.svelte +40 -50
  178. package/dist/components/sidebar/sidebar-root.svelte +39 -39
  179. package/dist/components/sidebar/sidebar.stories.svelte +43 -43
  180. package/dist/components/sidebar/types.d.ts +2 -12
  181. package/dist/components/tabs/tabs.stories.svelte +56 -56
  182. package/dist/components/textarea/atoms.d.ts +1 -0
  183. package/dist/components/textarea/atoms.js +1 -0
  184. package/dist/components/textarea/textarea-input.svelte +4 -1
  185. package/dist/components/textarea/textarea-root.svelte +2 -2
  186. package/dist/components/textarea/textarea-root.svelte.d.ts +2 -0
  187. package/dist/components/tooltip/tooltip-trigger.svelte +39 -39
  188. package/dist/components/tooltip/tooltip-trigger.svelte.d.ts +1 -0
  189. package/dist/components/tooltip/tooltip.stories.svelte +32 -32
  190. package/dist/components/tree/index.d.ts +1 -0
  191. package/dist/components/tree/index.js +1 -0
  192. package/dist/components/tree/motion.svelte.d.ts +6 -0
  193. package/dist/components/tree/motion.svelte.js +14 -0
  194. package/dist/components/tree/tree-body.svelte +4 -3
  195. package/dist/components/tree/tree.stories.svelte +142 -142
  196. package/dist/context/preset.svelte.d.ts +3 -1
  197. package/dist/icons/icon-copy.svelte +6 -0
  198. package/dist/icons/icon-copy.svelte.d.ts +26 -0
  199. package/dist/utils/dom.svelte.d.ts +2 -0
  200. package/dist/utils/dom.svelte.js +21 -0
  201. package/dist/utils/function.d.ts +1 -1
  202. package/dist/utils/promise.svelte.d.ts +5 -0
  203. package/dist/utils/promise.svelte.js +20 -0
  204. package/package.json +4 -3
  205. package/dist/components/combobox/compobox-item.svelte.d.ts +0 -34
  206. package/dist/components/combobox/compobox.stories.svelte +0 -51
  207. package/dist/components/combobox/compobox.stories.svelte.d.ts +0 -3
  208. package/dist/components/dropdown/item/bond.svelte.d.ts +0 -42
  209. package/dist/components/dropdown/item/bond.svelte.js +0 -99
  210. package/dist/components/menu/menu-item.svelte +0 -51
  211. package/dist/components/menu/menu-item.svelte.d.ts +0 -36
  212. package/dist/components/radio/types.svelte +0 -0
  213. package/llm/composition.md +0 -395
  214. package/llm/crafting.md +0 -838
  215. package/llm/motion.md +0 -970
  216. package/llm/philosophy.md +0 -23
  217. package/llm/preset-variant-integration.md +0 -516
  218. package/llm/preset.md +0 -383
  219. package/llm/styling.md +0 -216
  220. package/llm/usage.md +0 -46
  221. package/llm/variants.md +0 -1259
@@ -1,64 +1,64 @@
1
- <script module>
2
- import { defineMeta } from '@storybook/addon-svelte-csf';
3
- import { Dialog as ADialog } from '.';
4
- import { Dropdown } from '../dropdown';
5
- import { dialog } from './attachements.svelte';
6
-
7
- // More on how to set up stories at: https://storybook.js.org/docs/writing-stories
8
- const { Story } = defineMeta({
9
- title: 'Atoms/Dialog',
10
- // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
11
-
12
- parameters: {
13
- // More on how to position stories at: https://storybook.js.org/docs/configure/story-layout
14
- layout: 'fullscreen'
15
- },
16
- args: {}
17
- });
18
- </script>
19
-
20
- <script lang="ts">
21
- let isDialogOpen = $state(false);
22
- let isDropdownOpen = $state(false);
23
- </script>
24
-
25
- <Story name="Dialog" args={{}}>
26
- <div class="size-10 bg-red-500"></div>
27
- <button onclick={() => (isDialogOpen = !isDialogOpen)}>Open Dialog</button>
28
-
29
- <ADialog.Root class="bg-neutral-900/20" bind:open={isDialogOpen}>
30
- <ADialog.Content>
31
- <ADialog.Header>
32
- <div>Open Popover</div>
33
- <ADialog.CloseButton class="ml-auto"></ADialog.CloseButton>
34
- </ADialog.Header>
35
-
36
- <ADialog.Body>
37
- <p
38
- {@attach dialog((node, atom) => {
39
- console.log(atom);
40
- })}
41
- >
42
- Mauris et habitasse cubilia potenti at condimentum iaculis nam. Ante fusce litora
43
- tristique letius libero. Curabitur vitae cursus consectetur feugiat aenean viverra vel
44
- dolor diam nascetur.
45
- </p>
46
-
47
- <Dropdown.Root open={isDialogOpen && isDropdownOpen} class="w-full">
48
- <Dropdown.Trigger>Hello World</Dropdown.Trigger>
49
- <Dropdown.List>
50
- <Dropdown.Item id="ar">Arabic</Dropdown.Item>
51
- <Dropdown.Item id="en">English</Dropdown.Item>
52
- <Dropdown.Item id="sp">Spanish</Dropdown.Item>
53
- <Dropdown.Item id="it">Italian</Dropdown.Item>
54
- </Dropdown.List>
55
- </Dropdown.Root>
56
- </ADialog.Body>
57
-
58
- <ADialog.Footer class="gap-4">
59
- <button onclick={() => (isDialogOpen = false)}>Cancel</button>
60
- <button>Save</button>
61
- </ADialog.Footer>
62
- </ADialog.Content>
63
- </ADialog.Root>
64
- </Story>
1
+ <script module>
2
+ import { defineMeta } from '@storybook/addon-svelte-csf';
3
+ import { Dialog as ADialog } from '.';
4
+ import { Dropdown } from '../dropdown';
5
+ import { dialog } from './attachements.svelte';
6
+
7
+ // More on how to set up stories at: https://storybook.js.org/docs/writing-stories
8
+ const { Story } = defineMeta({
9
+ title: 'Atoms/Dialog',
10
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
11
+
12
+ parameters: {
13
+ // More on how to position stories at: https://storybook.js.org/docs/configure/story-layout
14
+ layout: 'fullscreen'
15
+ },
16
+ args: {}
17
+ });
18
+ </script>
19
+
20
+ <script lang="ts">
21
+ let isDialogOpen = $state(false);
22
+ let isDropdownOpen = $state(false);
23
+ </script>
24
+
25
+ <Story name="Dialog" args={{}}>
26
+ <div class="size-10 bg-red-500"></div>
27
+ <button onclick={() => (isDialogOpen = !isDialogOpen)}>Open Dialog</button>
28
+
29
+ <ADialog.Root class="bg-neutral-900/20" bind:open={isDialogOpen}>
30
+ <ADialog.Content>
31
+ <ADialog.Header>
32
+ <div>Open Popover</div>
33
+ <ADialog.CloseButton class="ml-auto"></ADialog.CloseButton>
34
+ </ADialog.Header>
35
+
36
+ <ADialog.Body>
37
+ <p
38
+ {@attach dialog((node, atom) => {
39
+ console.log(atom);
40
+ })}
41
+ >
42
+ Mauris et habitasse cubilia potenti at condimentum iaculis nam. Ante fusce litora
43
+ tristique letius libero. Curabitur vitae cursus consectetur feugiat aenean viverra vel
44
+ dolor diam nascetur.
45
+ </p>
46
+
47
+ <Dropdown.Root open={isDialogOpen && isDropdownOpen} class="w-full">
48
+ <Dropdown.Trigger>Hello World</Dropdown.Trigger>
49
+ <Dropdown.List>
50
+ <Dropdown.Item id="ar">Arabic</Dropdown.Item>
51
+ <Dropdown.Item id="en">English</Dropdown.Item>
52
+ <Dropdown.Item id="sp">Spanish</Dropdown.Item>
53
+ <Dropdown.Item id="it">Italian</Dropdown.Item>
54
+ </Dropdown.List>
55
+ </Dropdown.Root>
56
+ </ADialog.Body>
57
+
58
+ <ADialog.Footer class="gap-4">
59
+ <button onclick={() => (isDialogOpen = false)}>Cancel</button>
60
+ <button>Save</button>
61
+ </ADialog.Footer>
62
+ </ADialog.Content>
63
+ </ADialog.Root>
64
+ </Story>
@@ -0,0 +1,13 @@
1
+ type AnimateDialogRootParams = {
2
+ duration?: number;
3
+ delay?: number;
4
+ ease?: string;
5
+ };
6
+ export declare function animateDialogRoot(params?: AnimateDialogRootParams): (node: HTMLElement) => void;
7
+ type AnimateDialogContentParams = {
8
+ duration?: number;
9
+ delay?: number;
10
+ ease?: string;
11
+ };
12
+ export declare function animateDialogContent(params?: AnimateDialogContentParams): (node: HTMLElement) => void;
13
+ export {};
@@ -0,0 +1,44 @@
1
+ import { DURATION } from '../../shared';
2
+ import { animate } from 'motion';
3
+ import { DialogBond } from './bond.svelte';
4
+ export function animateDialogRoot(params = {}) {
5
+ const { duration = DURATION.fast / 1000, delay = 0, ease = 'anticipate' } = params;
6
+ return (node) => {
7
+ const bond = DialogBond.get();
8
+ const isOpen = bond?.state.props.open ?? false;
9
+ if (node instanceof HTMLDialogElement) {
10
+ node.show();
11
+ }
12
+ animate(node, {
13
+ opacity: +isOpen
14
+ }, {
15
+ duration,
16
+ delay,
17
+ ease
18
+ });
19
+ };
20
+ }
21
+ export function animateDialogContent(params = {}) {
22
+ const { duration = DURATION.fast / 1000, delay = 0, ease = 'anticipate' } = params;
23
+ const bond = DialogBond.get();
24
+ return (node) => {
25
+ const isOpen = bond?.state.props.open ?? false;
26
+ if (!bond?.elements.root.open) {
27
+ if (node instanceof HTMLDialogElement) {
28
+ node.show();
29
+ }
30
+ }
31
+ animate(node, { scale: 0.9 + 0.1 * +isOpen, opacity: +isOpen }, {
32
+ duration,
33
+ easing: ease,
34
+ delay,
35
+ onComplete: () => {
36
+ if (!isOpen) {
37
+ if (node instanceof HTMLDialogElement) {
38
+ node.close();
39
+ }
40
+ }
41
+ }
42
+ });
43
+ };
44
+ }
@@ -3,4 +3,4 @@ export declare function drawer(callback: (node: HTMLElement, bond?: DrawerBond)
3
3
  export declare function toggleDrawer(onclick?: (ev: MouseEvent) => void): (node: HTMLElement) => () => void;
4
4
  export declare function openDrawer(onclick?: (ev: MouseEvent) => void): (node: HTMLElement) => () => void;
5
5
  export declare function closeDrawer(onclick?: (ev: MouseEvent) => void): (node: HTMLElement) => (() => void) | undefined;
6
- export declare function clickoutDrawer(onclickout?: (ev: PointerEvent) => void): (node: Element) => () => void;
6
+ export declare function clickoutDrawer(onclickout?: (ev: PointerEvent, bond?: DrawerBond) => void): (node: Element) => () => void;
@@ -1,4 +1,3 @@
1
- import { on } from '../../attachments/event.svelte';
2
1
  import { clickout } from '../../attachments/clickout.svelte';
3
2
  import { DrawerBond } from './bond.svelte';
4
3
  export function drawer(callback) {
@@ -68,8 +67,7 @@ export function clickoutDrawer(onclickout) {
68
67
  if (bond.elements.content?.contains(target)) {
69
68
  return;
70
69
  }
71
- console.log('clickoutDrawer invoked', bond.state.props.open);
72
- onclickout?.(ev);
70
+ onclickout?.(ev, bond);
73
71
  if (ev.defaultPrevented) {
74
72
  return;
75
73
  }
@@ -18,42 +18,63 @@ export type DrawerBondElements = {
18
18
  export declare class DrawerBond<Props extends DrawerBondProps = DrawerBondProps, State extends DrawerBondState<Props> = DrawerBondState<Props>> extends Bond<Props, State, DrawerBondElements> {
19
19
  static CONTEXT_KEY: string;
20
20
  constructor(state: State);
21
- root(props?: Record<string, unknown>): {
21
+ root(): {
22
+ [x: symbol]: (node: HTMLElement) => void;
22
23
  id: string;
23
- 'aria-expanded': boolean;
24
- 'aria-disabled': boolean;
24
+ role: string;
25
+ 'aria-modal': boolean;
25
26
  'aria-labelledby': string | undefined;
27
+ 'aria-describedby': string | undefined;
28
+ 'aria-hidden': boolean;
29
+ inert: string | undefined;
30
+ tabindex: number;
26
31
  'data-active': boolean;
32
+ 'data-open': boolean;
27
33
  'data-kind': string;
34
+ onkeydown: (ev: KeyboardEvent) => void;
28
35
  };
29
- content(props?: Record<string, unknown>): {
36
+ content(): {
37
+ [x: symbol]: (node: HTMLElement) => void;
30
38
  id: string;
39
+ role: string;
31
40
  'data-kind': string;
32
41
  };
33
- body(props?: Record<string, unknown>): {
42
+ body(): {
43
+ [x: symbol]: (node: HTMLElement) => void;
34
44
  id: string;
35
45
  role: string;
36
46
  'data-kind': string;
37
47
  };
38
- header(props?: Record<string, unknown>): {
48
+ header(): {
49
+ [x: symbol]: (node: HTMLElement) => void;
39
50
  id: string;
40
51
  role: string;
41
52
  'data-kind': string;
42
53
  };
43
- title(props?: Record<string, unknown>): {
54
+ title(): {
55
+ [x: symbol]: (node: HTMLElement) => void;
44
56
  id: string;
57
+ role: string;
58
+ 'aria-level': number;
45
59
  'data-kind': string;
46
60
  };
47
- description(props?: Record<string, unknown>): {
61
+ description(): {
62
+ [x: symbol]: (node: HTMLElement) => void;
48
63
  id: string;
49
64
  'data-kind': string;
50
65
  };
51
- footer(props?: Record<string, unknown>): {
66
+ footer(): {
67
+ [x: symbol]: (node: HTMLElement) => void;
52
68
  id: string;
69
+ role: string;
53
70
  'data-kind': string;
54
71
  };
55
72
  backdrop(): {
56
73
  [x: symbol]: (node: HTMLElement) => void;
74
+ role: string;
75
+ 'aria-hidden': boolean;
76
+ 'data-kind': string;
77
+ onclick: (ev: MouseEvent) => void;
57
78
  };
58
79
  share(): this;
59
80
  static get(): DrawerBond | undefined;
@@ -17,102 +17,158 @@ export class DrawerBond extends Bond {
17
17
  constructor(state) {
18
18
  super(state);
19
19
  }
20
- root(props = {}) {
20
+ root() {
21
21
  const id = getElementId(this.id, DRAWER_ELEMENTS_KIND.root);
22
- const drawerHeaderId = getElementId(this.id, DRAWER_ELEMENTS_KIND.header);
23
22
  const drawerTitleId = getElementId(this.id, DRAWER_ELEMENTS_KIND.title);
24
- const haveHeaderElement = !!this.elements.header;
23
+ const drawerDescriptionId = getElementId(this.id, DRAWER_ELEMENTS_KIND.description);
24
+ const haveDescriptionElement = !!this.elements.description;
25
25
  const haveTitleElement = !!this.elements.title;
26
26
  const isOpen = this.state?.props?.open ?? false;
27
27
  const isDisabled = this.state?.props?.disabled ?? false;
28
28
  const isActive = isOpen && !isDisabled;
29
+ // Focus trap handler
30
+ const focusTrap = (ev) => {
31
+ const node = ev.currentTarget;
32
+ if (ev.key === 'Tab') {
33
+ const focusableElements = node.querySelectorAll('a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])');
34
+ const firstElement = focusableElements[0];
35
+ const lastElement = focusableElements[focusableElements.length - 1];
36
+ if (focusableElements.length === 0)
37
+ return;
38
+ if (ev.shiftKey && document.activeElement === firstElement) {
39
+ ev.preventDefault();
40
+ lastElement?.focus();
41
+ }
42
+ else if (!ev.shiftKey && document.activeElement === lastElement) {
43
+ ev.preventDefault();
44
+ firstElement?.focus();
45
+ }
46
+ }
47
+ };
48
+ let previousActiveElement = null;
29
49
  return {
30
50
  id: id,
31
- 'aria-expanded': isOpen,
32
- 'aria-disabled': isDisabled,
33
- 'aria-labelledby': haveTitleElement
34
- ? drawerTitleId
35
- : haveHeaderElement
36
- ? drawerHeaderId
37
- : undefined,
51
+ role: 'dialog',
52
+ 'aria-modal': true,
53
+ 'aria-labelledby': haveTitleElement ? drawerTitleId : undefined,
54
+ 'aria-describedby': haveDescriptionElement ? drawerDescriptionId : undefined,
55
+ 'aria-hidden': !isActive,
56
+ inert: !isActive ? '' : undefined,
57
+ tabindex: -1,
38
58
  'data-active': isActive,
59
+ 'data-open': isOpen,
39
60
  'data-kind': DRAWER_ELEMENTS_KIND.root,
40
- ...props,
61
+ onkeydown: (ev) => {
62
+ focusTrap(ev);
63
+ // Close on Escape key
64
+ if (ev.key === 'Escape' && !isDisabled) {
65
+ ev.preventDefault();
66
+ this.state.close();
67
+ }
68
+ },
41
69
  [createAttachmentKey()]: (node) => {
42
70
  this.elements.root = node;
71
+ if (this.state.props.open) {
72
+ // Store current focus
73
+ previousActiveElement = document.activeElement;
74
+ // Focus first focusable element or drawer itself
75
+ setTimeout(() => {
76
+ const firstFocusable = node.querySelector('a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])');
77
+ if (firstFocusable) {
78
+ firstFocusable.focus();
79
+ }
80
+ else {
81
+ node.focus();
82
+ }
83
+ }, 0);
84
+ }
85
+ else {
86
+ // Restore focus to previous element
87
+ if (previousActiveElement instanceof HTMLElement) {
88
+ previousActiveElement.focus();
89
+ }
90
+ }
43
91
  }
44
92
  };
45
93
  }
46
- content(props = {}) {
94
+ content() {
47
95
  const id = getElementId(this.id, DRAWER_ELEMENTS_KIND.content);
48
96
  return {
49
97
  id: id,
98
+ role: 'document',
50
99
  'data-kind': DRAWER_ELEMENTS_KIND.content,
51
- ...props,
52
100
  [createAttachmentKey()]: (node) => {
53
101
  this.elements.content = node;
54
102
  }
55
103
  };
56
104
  }
57
- body(props = {}) {
105
+ body() {
58
106
  const id = getElementId(this.id, DRAWER_ELEMENTS_KIND.body);
59
107
  return {
60
108
  id: id,
61
109
  role: 'region',
62
110
  'data-kind': DRAWER_ELEMENTS_KIND.body,
63
- ...props,
64
111
  [createAttachmentKey()]: (node) => {
65
112
  this.elements.body = node;
66
113
  }
67
114
  };
68
115
  }
69
- header(props = {}) {
116
+ header() {
70
117
  const id = getElementId(this.id, DRAWER_ELEMENTS_KIND.header);
71
118
  return {
72
119
  id: id,
73
- role: 'heading',
120
+ role: 'banner',
74
121
  'data-kind': DRAWER_ELEMENTS_KIND.header,
75
- ...props,
76
122
  [createAttachmentKey()]: (node) => {
77
123
  this.elements.header = node;
78
124
  }
79
125
  };
80
126
  }
81
- title(props = {}) {
127
+ title() {
82
128
  const id = getElementId(this.id, DRAWER_ELEMENTS_KIND.title);
83
129
  return {
84
130
  id: id,
131
+ role: 'heading',
132
+ 'aria-level': 2,
85
133
  'data-kind': DRAWER_ELEMENTS_KIND.title,
86
- ...props,
87
134
  [createAttachmentKey()]: (node) => {
88
135
  this.elements.title = node;
89
136
  }
90
137
  };
91
138
  }
92
- description(props = {}) {
139
+ description() {
93
140
  const id = getElementId(this.id, DRAWER_ELEMENTS_KIND.description);
94
141
  return {
95
142
  id: id,
96
143
  'data-kind': DRAWER_ELEMENTS_KIND.description,
97
- ...props,
98
144
  [createAttachmentKey()]: (node) => {
99
145
  this.elements.description = node;
100
146
  }
101
147
  };
102
148
  }
103
- footer(props = {}) {
149
+ footer() {
104
150
  const id = getElementId(this.id, DRAWER_ELEMENTS_KIND.footer);
105
151
  return {
106
152
  id: id,
153
+ role: 'contentinfo',
107
154
  'data-kind': DRAWER_ELEMENTS_KIND.footer,
108
- ...props,
109
155
  [createAttachmentKey()]: (node) => {
110
156
  this.elements.footer = node;
111
157
  }
112
158
  };
113
159
  }
114
160
  backdrop() {
161
+ const isDisabled = this.state?.props?.disabled ?? false;
115
162
  return {
163
+ role: 'presentation',
164
+ 'aria-hidden': true,
165
+ 'data-kind': DRAWER_ELEMENTS_KIND.backdrop,
166
+ onclick: (ev) => {
167
+ // Close drawer on backdrop click
168
+ if (!isDisabled) {
169
+ this.state.close();
170
+ }
171
+ },
116
172
  [createAttachmentKey()]: (node) => {
117
173
  this.elements.backdrop = node;
118
174
  }
@@ -1,57 +1,49 @@
1
- <script lang="ts" generics="E extends keyof HTMLElementTagNameMap = 'div', B extends Base = Base">
2
- import type { HTMLAttributes } from 'svelte/elements';
3
- import { HtmlAtom, type Base } from '../atom';
4
- import type { SlideoverContentProps } from './types';
5
- import { DrawerBond } from './bond.svelte';
6
- import { animate as motion } from 'motion';
7
-
8
- type Element = HTMLElementTagNameMap[E];
9
-
10
- const bond = DrawerBond.get();
11
- const isOpen = $derived(bond?.state.props.open);
12
-
13
- let {
14
- class: klass = '',
15
- children = undefined,
16
- onmount = undefined,
17
- ondestroy = undefined,
18
- animate = _animate,
19
- enter = undefined,
20
- exit = undefined,
21
- initial = _initial,
22
- ...restProps
23
- }: SlideoverContentProps<E, B> & HTMLAttributes<Element> = $props();
24
-
25
- const contentProps = $derived({
26
- ...bond?.content(),
27
- ...restProps
28
- });
29
-
30
- function _initial(node: HTMLElement) {
31
- motion(node, { x: isOpen ? 0 : -100 + '%', left: 0 }, { duration: 0.3, ease: 'anticipate' });
32
- }
33
-
34
- function _animate(node: HTMLElement) {
35
- motion(node, { x: isOpen ? 0 : -100 + '%', left: 0 }, { duration: 0.3, ease: 'anticipate' });
36
- }
37
- </script>
38
-
39
- <HtmlAtom
40
- preset="drawer.content"
41
- class={[
42
- 'bg-card text-foreground border-border pointer-events-none absolute',
43
- isOpen && 'pointer-events-auto',
44
- '$preset',
45
- klass
46
- ]}
47
- {bond}
48
- onmount={onmount?.bind(bond.state)}
49
- ondestroy={ondestroy?.bind(bond.state)}
50
- enter={enter?.bind(bond.state)}
51
- exit={exit?.bind(bond.state)}
52
- initial={initial?.bind(bond.state)}
53
- animate={animate?.bind(bond.state)}
54
- {...contentProps}
55
- >
56
- {@render children?.({ drawer: bond })}
57
- </HtmlAtom>
1
+ <script lang="ts" generics="E extends keyof HTMLElementTagNameMap = 'div', B extends Base = Base">
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ import { HtmlAtom as Atom, type Base } from '../atom';
4
+ import type { SlideoverContentProps } from './types';
5
+ import { DrawerBond } from './bond.svelte';
6
+ import { animateDrawerContent } from './motion';
7
+
8
+ type Element = HTMLElementTagNameMap[E];
9
+
10
+ const bond = DrawerBond.get();
11
+ const isOpen = $derived(bond?.state.props.open);
12
+
13
+ let {
14
+ class: klass = '',
15
+ children = undefined,
16
+ onmount = undefined,
17
+ ondestroy = undefined,
18
+ animate = animateDrawerContent({ ease: 'easeOut', side: 'left' }),
19
+ enter = undefined,
20
+ exit = undefined,
21
+ initial = animateDrawerContent({ ease: 'easeOut', side: 'left', duration: 0 }),
22
+ ...restProps
23
+ }: SlideoverContentProps<E, B> & HTMLAttributes<Element> = $props();
24
+
25
+ const contentProps = $derived({
26
+ ...bond?.content(),
27
+ ...restProps
28
+ });
29
+ </script>
30
+
31
+ <Atom
32
+ preset="drawer.content"
33
+ class={[
34
+ 'bg-card text-foreground border-border pointer-events-none absolute',
35
+ isOpen && 'pointer-events-auto',
36
+ '$preset',
37
+ klass
38
+ ]}
39
+ {bond}
40
+ onmount={onmount?.bind(bond.state)}
41
+ ondestroy={ondestroy?.bind(bond.state)}
42
+ enter={enter?.bind(bond.state)}
43
+ exit={exit?.bind(bond.state)}
44
+ initial={initial?.bind(bond.state)}
45
+ animate={animate?.bind(bond.state)}
46
+ {...contentProps}
47
+ >
48
+ {@render children?.({ drawer: bond })}
49
+ </Atom>
@@ -6,6 +6,7 @@
6
6
  import { DrawerBond, DrawerBondState, type DrawerBondProps } from './bond.svelte';
7
7
  import type { SlideoverRootProps } from './types';
8
8
  import { ActivePortal } from '../portal';
9
+ import { animateDrawerRoot } from './motion';
9
10
 
10
11
  type Element = HTMLElementTagNameMap[E];
11
12
 
@@ -13,16 +14,16 @@
13
14
  open = $bindable(false),
14
15
  children = undefined,
15
16
  class: klass = '',
16
- as = 'dialog',
17
+ as = 'div',
17
18
  disabled = false,
18
19
  portal = undefined,
19
20
  onclose = undefined,
20
21
  onmount = undefined,
21
22
  ondestroy = undefined,
22
- animate = undefined,
23
23
  enter = undefined,
24
24
  exit = undefined,
25
- initial = undefined,
25
+ initial = animateDrawerRoot({ duration: 0 }),
26
+ animate = animateDrawerRoot({}),
26
27
  factory = _factory,
27
28
  ...restProps
28
29
  }: SlideoverRootProps<E, B> & HTMLAttributes<Element> = $props();
@@ -73,7 +74,7 @@
73
74
  portal={portal ?? 'root.l1'}
74
75
  preset="drawer"
75
76
  class={[
76
- 'border-border pointer-events-auto h-full w-full overflow-hidden bg-transparent',
77
+ 'border-border pointer-events-none fixed inset-0 h-full w-full overflow-hidden bg-transparent',
77
78
  !open && 'pointer-events-none',
78
79
  '$preset',
79
80
  klass