@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,8 +1,7 @@
1
1
  import { getContext, setContext, untrack } from 'svelte';
2
2
  import { createAttachmentKey } from 'svelte/attachments';
3
3
  import { autoUpdate, computePosition, arrow, flip, offset } from '@floating-ui/dom';
4
- import { debounce } from 'es-toolkit';
5
- import { getElementId, isBrowser } from '../../utils/dom.svelte.js';
4
+ import { focus, focusTrap, getElementId, isBrowser } from '../../utils/dom.svelte.js';
6
5
  import { Bond, BondState } from '../../shared/bond.svelte.js';
7
6
  const POPOVER_ELEMENTS_KIND = {
8
7
  trigger: 'popover-trigger',
@@ -12,14 +11,11 @@ const POPOVER_ELEMENTS_KIND = {
12
11
  };
13
12
  export class PopoverBond extends Bond {
14
13
  static CONTEXT_KEY = '@atomic-sv/bonds/popover';
15
- #position = $state();
14
+ position = $state();
16
15
  constructor(state) {
17
16
  super(state);
18
17
  }
19
- get position() {
20
- return this.#position;
21
- }
22
- trigger(props = {}) {
18
+ trigger() {
23
19
  const isButtonElement = isBrowser()
24
20
  ? this.elements.trigger instanceof HTMLButtonElement
25
21
  : false;
@@ -30,12 +26,13 @@ export class PopoverBond extends Bond {
30
26
  const overlayId = getElementId(this.id, POPOVER_ELEMENTS_KIND.content);
31
27
  return {
32
28
  id,
33
- role: isButtonElement ? '' : 'button', // Ensure trigger is announced as a button
29
+ role: isButtonElement ? '' : 'button',
34
30
  disabled: isButtonElement ? isDisabled : undefined,
35
- tabindex: isDisabled ? -1 : 0, // Make focusable unless disabled
31
+ tabindex: isDisabled ? -1 : 0,
36
32
  'aria-expanded': isOpen,
37
33
  'aria-disabled': isDisabled,
38
34
  'aria-controls': overlayId,
35
+ 'aria-haspopup': 'dialog',
39
36
  'data-kind': kind,
40
37
  onclick: (ev) => {
41
38
  if (ev.button === 2) {
@@ -46,16 +43,30 @@ export class PopoverBond extends Bond {
46
43
  }
47
44
  this.state.toggle();
48
45
  },
49
- ...props,
46
+ onkeydown: (ev) => {
47
+ if (isDisabled)
48
+ return;
49
+ // Toggle on Enter or Space
50
+ if (ev.key === 'Enter' || ev.key === ' ') {
51
+ this.state.toggle();
52
+ return;
53
+ }
54
+ if (ev.key === 'Tab') {
55
+ this.elements.content?.focus();
56
+ return;
57
+ }
58
+ if (ev.key === 'Escape') {
59
+ this.state.close();
60
+ }
61
+ },
50
62
  [createAttachmentKey()]: (node) => {
51
63
  this.elements.trigger = node;
52
- const position = untrack(() => this.#position);
64
+ const position = untrack(() => this.position);
53
65
  if (!position) {
54
66
  const init = async () => {
55
67
  popover(this)({
56
- ...props,
57
- onchange: (node, position) => {
58
- this.#position = position;
68
+ onchange: (_node, position) => {
69
+ this.position = position;
59
70
  }
60
71
  });
61
72
  const pointerLeaveHandler = () => {
@@ -72,61 +83,85 @@ export class PopoverBond extends Bond {
72
83
  }
73
84
  };
74
85
  }
75
- content(props = {}) {
86
+ content() {
76
87
  const kind = POPOVER_ELEMENTS_KIND.content;
77
88
  const id = getElementId(this.id, kind);
78
89
  const triggerId = getElementId(this.id, POPOVER_ELEMENTS_KIND.trigger);
79
90
  const isOpen = this.state?.props?.open ?? false;
80
91
  const isDisabled = this.state?.props?.disabled ?? false;
81
92
  const isActive = isOpen && !isDisabled;
93
+ // Focus management
94
+ const focusManager = (ev) => {
95
+ if (ev.key === 'Escape') {
96
+ this.state.close();
97
+ this.elements.trigger?.focus();
98
+ return;
99
+ }
100
+ focusTrap(ev);
101
+ };
82
102
  return {
83
103
  id,
84
- role: 'dialog', // Announce as dialog
85
- 'aria-modal': true, // Modal dialog
86
- 'aria-labelledby': triggerId, // Link overlay to trigger
87
- 'aria-controlledby': triggerId, // Link overlay to trigger
104
+ role: 'dialog',
105
+ 'aria-modal': false,
106
+ 'aria-labelledby': triggerId,
107
+ inert: !isActive ? true : undefined,
108
+ tabindex: -1,
88
109
  'data-atom': this.id,
89
- 'data-kind': 'overlay',
110
+ 'data-kind': 'content',
90
111
  'data-active': isActive,
91
- ...props,
112
+ onkeydown: isOpen ? focusManager : undefined,
92
113
  [createAttachmentKey()]: (node) => {
93
114
  this.elements.content = node;
94
- if (!this.elements.trigger) {
115
+ const triggerElement = this.elements.trigger;
116
+ if (!triggerElement) {
95
117
  return;
96
118
  }
97
- if (!this.state.isOpen)
119
+ if (!this.state.isOpen) {
98
120
  return;
99
- return popover(this)({
100
- ...props,
121
+ }
122
+ // check if trigger contains a focusable element and the focus is already inside
123
+ // check if the in-focus element is an input, textarea or select to avoid stealing focus
124
+ const activeElement = document.activeElement;
125
+ const triggerContainsFocus = ['input', 'textarea'].includes(activeElement.tagName.toLowerCase()) ||
126
+ triggerElement === activeElement ||
127
+ triggerElement.contains(activeElement);
128
+ // Move focus to popover when opened
129
+ if (!triggerContainsFocus) {
130
+ setTimeout(() => focus(node), 0);
131
+ }
132
+ const cleanup = popover(this)({
101
133
  onchange: (node, position) => {
102
- this.#position = position;
103
- props.onchange?.(node, position);
134
+ this.position = position;
104
135
  }
105
136
  }, autoUpdate);
137
+ return () => {
138
+ cleanup?.();
139
+ };
106
140
  }
107
141
  };
108
142
  }
109
- indicator(props = {}) {
143
+ indicator() {
110
144
  const kind = POPOVER_ELEMENTS_KIND.indicator;
111
145
  const id = getElementId(this.id, kind);
112
- const triggerId = getElementId(this.id, POPOVER_ELEMENTS_KIND.trigger);
146
+ const isOpen = this.state?.props?.open ?? false;
113
147
  return {
114
148
  id,
115
- 'aria-controlledby': triggerId,
149
+ 'aria-hidden': true,
150
+ 'aria-live': isOpen ? 'polite' : 'off',
116
151
  'data-kind': kind,
117
- ...props,
118
152
  [createAttachmentKey()]: (node) => {
119
153
  this.elements.indicator = node;
120
154
  }
121
155
  };
122
156
  }
123
- arrow(props = {}) {
157
+ arrow() {
124
158
  const kind = POPOVER_ELEMENTS_KIND.arrow;
125
159
  const id = getElementId(this.id, kind);
126
160
  return {
127
161
  id: id,
162
+ role: 'presentation',
163
+ 'aria-hidden': true,
128
164
  'data-kind': kind,
129
- ...props,
130
165
  [createAttachmentKey()]: (node) => {
131
166
  this.elements.arrow = node;
132
167
  }
@@ -167,8 +202,6 @@ function popover(bond) {
167
202
  return;
168
203
  }
169
204
  const { content, trigger, arrow: arrowElement } = bond.elements;
170
- // Set minimum width to match trigger
171
- content.style.minWidth = `${trigger.clientWidth}px`;
172
205
  // Build middleware stack
173
206
  const middleware = [
174
207
  offset(ofs),
@@ -183,17 +216,20 @@ function popover(bond) {
183
216
  }
184
217
  // Debounce position change callback
185
218
  const onchangeCallback = props.onchange;
186
- const onchangeDebounced = debounce((node, position) => {
187
- onchangeCallback?.(node, position);
188
- }, 1000 / 60 // ~16ms for 60fps
189
- );
190
219
  // Compute position and notify listeners
191
220
  const compute = async () => {
221
+ // Wait for next frame to ensure DOM has settled and styles are applied
222
+ // Double requestAnimationFrame - This ensures the browser has completed both layout calculation AND painting, giving us accurate final dimensions
223
+ await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)));
192
224
  const position = await computePosition(trigger, content, {
193
225
  placement: placement ?? 'bottom',
194
226
  middleware
195
227
  });
196
- onchangeDebounced(content, position);
228
+ onchangeCallback?.(content, position);
229
+ // Set minimum width to match trigger
230
+ requestAnimationFrame(() => {
231
+ content.style.minWidth = `${trigger.clientWidth}px`;
232
+ });
197
233
  };
198
234
  // Use auto-update if provided, otherwise compute once
199
235
  if (updater) {
@@ -0,0 +1,6 @@
1
+ export type AnimatePopoverContentParams = {
2
+ duration?: number;
3
+ delay?: number;
4
+ ease?: string;
5
+ };
6
+ export declare function animatePopoverContent(params?: AnimatePopoverContentParams): (node: HTMLElement) => void;
@@ -0,0 +1,56 @@
1
+ import { animate } from 'motion';
2
+ import { PopoverBond } from '.';
3
+ import { DURATION } from '../../shared';
4
+ export function animatePopoverContent(params = {}) {
5
+ return (node) => {
6
+ const bond = PopoverBond.get();
7
+ const { duration = DURATION.quick / 1000, delay = 0, ease = 'easeInOut' } = params;
8
+ const isOpen = bond?.state.props.open ?? false;
9
+ const position = bond.position;
10
+ const placement = position?.placement;
11
+ const x = position?.x ?? 0;
12
+ const y = position?.y ?? 0;
13
+ const dy = placement?.startsWith('top') ? -1 : placement?.startsWith('bottom') ? 1 : 0;
14
+ const dx = placement?.startsWith('left') ? -1 : placement?.startsWith('right') ? 1 : 0;
15
+ const offset = bond.state.props.offset;
16
+ const xOffset = dx * offset;
17
+ const yOffset = dy * offset;
18
+ const openAsNumber = +isOpen;
19
+ const deltaArrow = position?.middlewareData?.arrow ? 1 : 0;
20
+ const arrowClientWidth = bond?.elements.arrow?.clientWidth ?? 0;
21
+ const arrowClientHeight = bond?.elements.arrow?.clientHeight ?? 0;
22
+ const getTransformOrigin = () => {
23
+ switch (placement) {
24
+ case 'top':
25
+ case 'top-start':
26
+ case 'top-end':
27
+ return 'bottom';
28
+ case 'bottom':
29
+ case 'bottom-start':
30
+ case 'bottom-end':
31
+ return 'top';
32
+ case 'left':
33
+ case 'left-start':
34
+ case 'left-end':
35
+ return 'right';
36
+ case 'right':
37
+ case 'right-start':
38
+ case 'right-end':
39
+ return 'left';
40
+ default:
41
+ return 'center';
42
+ }
43
+ };
44
+ const transformOrigin = getTransformOrigin();
45
+ const from = isOpen ? 1 : 0.95;
46
+ animate(node, {
47
+ opacity: openAsNumber,
48
+ y: dy * (!isOpen ? -1 : 0) * (arrowClientHeight + yOffset),
49
+ x: dx * (!isOpen ? -1 : 0) * (arrowClientWidth + xOffset),
50
+ scaleY: dy ? (isOpen ? [from, 1] : [1, 0.8]) : undefined,
51
+ scaleX: dx ? (isOpen ? [from, 1] : [1, 0.8]) : undefined,
52
+ transformOrigin
53
+ }, { duration, delay, ease });
54
+ animate(node, { opacity: +isOpen }, { duration, ease, delay });
55
+ };
56
+ }
@@ -1,111 +1,111 @@
1
- <script lang="ts" generics="E extends keyof HTMLElementTagNameMap = 'div', B extends Base = Base">
2
- import type { HTMLAttributes } from 'svelte/elements';
3
- import { animate as motion } from 'motion';
4
- import { HtmlAtom, type Base } from '../atom';
5
- import { PopoverBond } from './bond.svelte';
6
- import type { PopoverArrowProps } from './types';
7
-
8
- type Element = HTMLElementTagNameMap[E];
9
-
10
- const bond = PopoverBond.get();
11
-
12
- if (!bond) {
13
- throw new Error('');
14
- }
15
-
16
- let {
17
- class: klass = '',
18
- children = undefined,
19
- onmount = undefined,
20
- ondestroy = undefined,
21
- animate = _animate,
22
- enter = undefined,
23
- exit = undefined,
24
- initial = undefined,
25
- ...restProps
26
- }: PopoverArrowProps<E, B> & HTMLAttributes<Element> = $props();
27
-
28
- const position = $derived(bond.position);
29
- const middlewareArrowData = $derived(position?.middlewareData?.arrow);
30
- const isReady = $derived(!!middlewareArrowData);
31
- const side = $derived(position?.placement?.split('-')[0] ?? 'top');
32
-
33
- const arrowProps = $derived({
34
- ...bond.arrow(),
35
- ...restProps
36
- });
37
-
38
- // Rotation based on placement side
39
- const rotation = $derived.by(() => {
40
- switch (side) {
41
- case 'top':
42
- return 180;
43
- case 'bottom':
44
- return 0;
45
- case 'left':
46
- return 90;
47
- case 'right':
48
- return -90;
49
- default:
50
- return 0;
51
- }
52
- });
53
-
54
- function _animate(node: HTMLElement) {
55
- if (!middlewareArrowData) {
56
- return;
57
- }
58
-
59
- const { x, y } = middlewareArrowData;
60
-
61
- const isMainAxis = side === 'top' || side === 'bottom';
62
-
63
- const crossAxisStyle = isMainAxis
64
- ? {
65
- left: 0
66
- }
67
- : {
68
- top: 0
69
- };
70
-
71
- motion(
72
- node,
73
- {
74
- x: x ?? 0,
75
- y: y ?? 0,
76
- opacity: 1,
77
- ...crossAxisStyle
78
- },
79
- { duration: 0 }
80
- );
81
- }
82
- </script>
83
-
84
- <HtmlAtom
85
- {bond}
86
- preset="popover.arrow"
87
- class={['text-border border-border pointer-events-none absolute opacity-0', '$preset', klass]}
88
- onmount={onmount?.bind(bond.state)}
89
- ondestroy={ondestroy?.bind(bond.state)}
90
- animate={animate?.bind(bond.state)}
91
- enter={enter?.bind(bond.state)}
92
- exit={exit?.bind(bond.state)}
93
- initial={initial?.bind(bond.state)}
94
- style="{side}: 100%;"
95
- {...arrowProps}
96
- >
97
- {#if children}
98
- {@render children({ popover: bond })}
99
- {:else}
100
- <svg
101
- width="16"
102
- height="8"
103
- viewBox="0 0 16 8"
104
- fill="none"
105
- xmlns="http://www.w3.org/2000/svg"
106
- style="transform: rotate({rotation}deg);"
107
- >
108
- <path d="M0 8C2 8 6 4 8 0C10 4 14 8 16 8H0Z" fill="currentColor" />
109
- </svg>
110
- {/if}
111
- </HtmlAtom>
1
+ <script lang="ts" generics="E extends keyof HTMLElementTagNameMap = 'div', B extends Base = Base">
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ import { animate as motion } from 'motion';
4
+ import { HtmlAtom, type Base } from '../atom';
5
+ import { PopoverBond } from './bond.svelte';
6
+ import type { PopoverArrowProps } from './types';
7
+
8
+ type Element = HTMLElementTagNameMap[E];
9
+
10
+ const bond = PopoverBond.get();
11
+
12
+ if (!bond) {
13
+ throw new Error('');
14
+ }
15
+
16
+ let {
17
+ class: klass = '',
18
+ children = undefined,
19
+ onmount = undefined,
20
+ ondestroy = undefined,
21
+ animate = _animate,
22
+ enter = undefined,
23
+ exit = undefined,
24
+ initial = undefined,
25
+ ...restProps
26
+ }: PopoverArrowProps<E, B> & HTMLAttributes<Element> = $props();
27
+
28
+ const position = $derived(bond.position);
29
+ const middlewareArrowData = $derived(position?.middlewareData?.arrow);
30
+ const isReady = $derived(!!middlewareArrowData);
31
+ const side = $derived(position?.placement?.split('-')[0] ?? 'top');
32
+
33
+ const arrowProps = $derived({
34
+ ...bond.arrow(),
35
+ ...restProps
36
+ });
37
+
38
+ // Rotation based on placement side
39
+ const rotation = $derived.by(() => {
40
+ switch (side) {
41
+ case 'top':
42
+ return 180;
43
+ case 'bottom':
44
+ return 0;
45
+ case 'left':
46
+ return 90;
47
+ case 'right':
48
+ return -90;
49
+ default:
50
+ return 0;
51
+ }
52
+ });
53
+
54
+ function _animate(node: HTMLElement) {
55
+ if (!middlewareArrowData) {
56
+ return;
57
+ }
58
+
59
+ const { x, y } = middlewareArrowData;
60
+
61
+ const isMainAxis = side === 'top' || side === 'bottom';
62
+
63
+ const crossAxisStyle = isMainAxis
64
+ ? {
65
+ left: 0
66
+ }
67
+ : {
68
+ top: 0
69
+ };
70
+
71
+ motion(
72
+ node,
73
+ {
74
+ x: x ?? 0,
75
+ y: y ?? 0,
76
+ opacity: 1,
77
+ ...crossAxisStyle
78
+ },
79
+ { duration: 0 }
80
+ );
81
+ }
82
+ </script>
83
+
84
+ <HtmlAtom
85
+ {bond}
86
+ preset="popover.arrow"
87
+ class={['text-border border-border pointer-events-none absolute opacity-0', '$preset', klass]}
88
+ onmount={onmount?.bind(bond.state)}
89
+ ondestroy={ondestroy?.bind(bond.state)}
90
+ animate={animate?.bind(bond.state)}
91
+ enter={enter?.bind(bond.state)}
92
+ exit={exit?.bind(bond.state)}
93
+ initial={initial?.bind(bond.state)}
94
+ style="{side}: 100%;"
95
+ {...arrowProps}
96
+ >
97
+ {#if children}
98
+ {@render children({ popover: bond })}
99
+ {:else}
100
+ <svg
101
+ width="16"
102
+ height="8"
103
+ viewBox="0 0 16 8"
104
+ fill="none"
105
+ xmlns="http://www.w3.org/2000/svg"
106
+ style="transform: rotate({rotation}deg);"
107
+ >
108
+ <path d="M0 8C2 8 6 4 8 0C10 4 14 8 16 8H0Z" fill="currentColor" />
109
+ </svg>
110
+ {/if}
111
+ </HtmlAtom>