lapikit 0.7.3 → 0.7.4

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 (67) hide show
  1. package/dist/@types/components.d.ts +1 -7
  2. package/dist/@types/dom.d.ts +4 -3
  3. package/dist/@types/props.d.ts +3 -3
  4. package/dist/animations/ripple.js +8 -2
  5. package/dist/components/accordion/accordion.svelte +192 -228
  6. package/dist/components/accordion/accordion.types.d.ts +5 -7
  7. package/dist/components/accordion/modules/accordion-item.svelte +144 -140
  8. package/dist/components/alert/alert.svelte +246 -293
  9. package/dist/components/alert/alert.types.d.ts +5 -5
  10. package/dist/components/app/app.svelte +25 -43
  11. package/dist/components/appbar/appbar.svelte +205 -190
  12. package/dist/components/appbar/appbar.types.d.ts +6 -6
  13. package/dist/components/aspect-ratio/aspect-ratio.svelte +43 -63
  14. package/dist/components/avatar/avatar.svelte +169 -161
  15. package/dist/components/avatar/avatar.types.d.ts +3 -4
  16. package/dist/components/btn/btn.svelte +431 -465
  17. package/dist/components/btn/btn.types.d.ts +5 -6
  18. package/dist/components/card/card.svelte +24 -23
  19. package/dist/components/card/card.types.d.ts +2 -2
  20. package/dist/components/card/modules/card-content.svelte +4 -1
  21. package/dist/components/card/modules/card-title.svelte +4 -1
  22. package/dist/components/chip/chip.svelte +416 -464
  23. package/dist/components/chip/chip.types.d.ts +5 -5
  24. package/dist/components/dialog/dialog.svelte +174 -171
  25. package/dist/components/dialog/dialog.types.d.ts +6 -6
  26. package/dist/components/dropdown/dropdown.svelte +149 -144
  27. package/dist/components/dropdown/dropdown.types.d.ts +4 -11
  28. package/dist/components/field/field.svelte +474 -0
  29. package/dist/components/field/field.svelte.d.ts +4 -0
  30. package/dist/components/field/field.types.d.ts +46 -0
  31. package/dist/components/field/field.types.js +1 -0
  32. package/dist/components/icon/icon.svelte +67 -96
  33. package/dist/components/icon/icon.types.d.ts +2 -1
  34. package/dist/components/index.d.ts +2 -0
  35. package/dist/components/index.js +1 -0
  36. package/dist/components/list/list.svelte +193 -194
  37. package/dist/components/list/list.types.d.ts +5 -7
  38. package/dist/components/list/modules/list-item.svelte +134 -147
  39. package/dist/components/modal/modal.svelte +239 -222
  40. package/dist/components/modal/modal.types.d.ts +6 -6
  41. package/dist/components/popover/popover.svelte +125 -127
  42. package/dist/components/popover/popover.types.d.ts +4 -11
  43. package/dist/components/separator/separator.svelte +56 -87
  44. package/dist/components/separator/separator.types.d.ts +1 -1
  45. package/dist/components/spacer/spacer.svelte +6 -25
  46. package/dist/components/textfield/textfield.svelte +94 -543
  47. package/dist/components/textfield/textfield.types.d.ts +13 -33
  48. package/dist/components/toolbar/toolbar.svelte +216 -211
  49. package/dist/components/toolbar/toolbar.types.d.ts +8 -9
  50. package/dist/components/tooltip/tooltip.svelte +201 -266
  51. package/dist/components/tooltip/tooltip.types.d.ts +3 -10
  52. package/dist/constants.js +1 -0
  53. package/dist/html-mapped.d.ts +1 -15
  54. package/dist/html-mapped.js +0 -19
  55. package/dist/labs/sheet/sheet.svelte +27 -22
  56. package/dist/utils/color.d.ts +2 -2
  57. package/dist/utils/color.js +2 -1
  58. package/dist/utils/components.d.ts +10 -10
  59. package/dist/utils/components.js +16 -75
  60. package/dist/utils/floating.d.ts +58 -0
  61. package/dist/utils/floating.js +131 -0
  62. package/dist/utils/index.d.ts +1 -0
  63. package/dist/utils/index.js +1 -0
  64. package/package.json +3 -3
  65. package/dist/components/dropdown/dropdown.svelte.js +0 -148
  66. package/dist/components/popover/popover.svelte.js +0 -134
  67. package/dist/components/tooltip/tooltip.svelte.js +0 -131
@@ -72,7 +72,7 @@
72
72
  {@render children?.()}
73
73
  </svelte:element>
74
74
 
75
- <style>.kit-sheet {
75
+ <style>:where(.kit-sheet) {
76
76
  --kit-sheet-color: initial;
77
77
  --kit-sheet-on-color: initial;
78
78
  --kit-sheet-active-fg: initial;
@@ -93,12 +93,15 @@
93
93
  );
94
94
  }
95
95
  @supports (color: color(from red srgb-linear r g b)) {
96
- .kit-sheet {
96
+ :where(.kit-sheet) {
97
97
  --kit-sheet-contrast: color(from var(--kit-sheet-color) srgb-linear clamp(0, (0.1791 - (0.2126 * r + 0.7152 * g + 0.0722 * b)) * 100000, 1) clamp(0, (0.1791 - (0.2126 * r + 0.7152 * g + 0.0722 * b)) * 100000, 1) clamp(0, (0.1791 - (0.2126 * r + 0.7152 * g + 0.0722 * b)) * 100000, 1)/1);
98
98
  }
99
99
  }
100
- .kit-sheet {
100
+ :where(.kit-sheet) {
101
101
  --kit-sheet-p: 12px;
102
+ }
103
+
104
+ .kit-sheet {
102
105
  display: flex;
103
106
  position: relative;
104
107
  gap: calc(var(--kit-space-default) + var(--kit-sheet-density-offset) / 2);
@@ -129,7 +132,7 @@ button.kit-sheet {
129
132
  text-align: inherit;
130
133
  }
131
134
 
132
- .kit-sheet[data-variant=tonal] {
135
+ :where(.kit-sheet[data-variant=tonal]) {
133
136
  --kit-sheet-bg: color-mix(in oklab, var(--kit-sheet-ink) 16%, transparent);
134
137
  --kit-sheet-fg: var(--kit-sheet-ink);
135
138
  --kit-sheet-border: transparent;
@@ -137,7 +140,7 @@ button.kit-sheet {
137
140
  --kit-sheet-active-fg: var(--kit-sheet-ink);
138
141
  }
139
142
 
140
- .kit-sheet[data-variant=text] {
143
+ :where(.kit-sheet[data-variant=text]) {
141
144
  --kit-sheet-bg: transparent;
142
145
  --kit-sheet-fg: var(--kit-sheet-ink);
143
146
  --kit-sheet-border: transparent;
@@ -145,7 +148,7 @@ button.kit-sheet {
145
148
  --kit-sheet-active-fg: var(--kit-sheet-ink);
146
149
  }
147
150
 
148
- .kit-sheet[data-variant=plain] {
151
+ :where(.kit-sheet[data-variant=plain]) {
149
152
  --kit-sheet-bg: transparent;
150
153
  --kit-sheet-fg: var(--kit-sheet-ink);
151
154
  --kit-sheet-border: transparent;
@@ -157,25 +160,27 @@ button.kit-sheet {
157
160
  --kit-sheet-active-fg: var(--kit-sheet-color, var(--kit-color-accent));
158
161
  }
159
162
 
160
- .kit-sheet[data-variant=outline] {
163
+ :where(.kit-sheet[data-variant=outline]) {
161
164
  --kit-sheet-bg: transparent;
162
165
  --kit-sheet-fg: var(--kit-sheet-ink);
163
166
  --kit-sheet-border: var(--kit-sheet-color, var(--kit-color-border));
164
167
  --kit-sheet-active: color-mix(in oklab, var(--kit-sheet-ink) 14%, transparent);
165
168
  --kit-sheet-active-fg: var(--kit-sheet-ink);
166
- border: var(--kit-outline-weight) solid var(--kit-sheet-border);
167
169
  }
168
170
 
169
- .kit-sheet[data-variant=surface] {
171
+ :where(.kit-sheet[data-variant=surface]) {
170
172
  --kit-sheet-bg: color-mix(in oklab, var(--kit-sheet-ink) 8%, transparent);
171
173
  --kit-sheet-fg: var(--kit-sheet-ink);
172
174
  --kit-sheet-border: color-mix(in oklab, var(--kit-sheet-ink) 35%, transparent);
173
175
  --kit-sheet-active: color-mix(in oklab, var(--kit-sheet-ink) 18%, transparent);
174
176
  --kit-sheet-active-fg: var(--kit-sheet-ink);
177
+ }
178
+
179
+ .kit-sheet:is([data-variant=outline], [data-variant=surface]) {
175
180
  border: var(--kit-outline-weight) solid var(--kit-sheet-border);
176
181
  }
177
182
 
178
- .kit-sheet[data-active=true] {
183
+ :where(.kit-sheet[data-active=true]) {
179
184
  --kit-sheet-bg: var(--kit-sheet-active, var(--kit-color-accent));
180
185
  --kit-sheet-fg: var(
181
186
  --kit-sheet-active-fg,
@@ -213,31 +218,31 @@ button.kit-sheet {
213
218
  * rounded
214
219
  * @link https://lapikit.dev/docs/customize
215
220
  */
216
- .kit-sheet[data-rounded="0"] {
221
+ :where(.kit-sheet[data-rounded="0"]) {
217
222
  --kit-sheet-radius: var(--kit-shape-none);
218
223
  }
219
224
 
220
- .kit-sheet[data-rounded=xs] {
225
+ :where(.kit-sheet[data-rounded=xs]) {
221
226
  --kit-sheet-radius: var(--kit-shape-xs);
222
227
  }
223
228
 
224
- .kit-sheet[data-rounded=sm] {
229
+ :where(.kit-sheet[data-rounded=sm]) {
225
230
  --kit-sheet-radius: var(--kit-shape-sm);
226
231
  }
227
232
 
228
- .kit-sheet[data-rounded=md] {
233
+ :where(.kit-sheet[data-rounded=md]) {
229
234
  --kit-sheet-radius: var(--kit-shape-md);
230
235
  }
231
236
 
232
- .kit-sheet[data-rounded=lg] {
237
+ :where(.kit-sheet[data-rounded=lg]) {
233
238
  --kit-sheet-radius: var(--kit-shape-lg);
234
239
  }
235
240
 
236
- .kit-sheet[data-rounded=xl] {
241
+ :where(.kit-sheet[data-rounded=xl]) {
237
242
  --kit-sheet-radius: var(--kit-shape-xl);
238
243
  }
239
244
 
240
- .kit-sheet[data-rounded=full] {
245
+ :where(.kit-sheet[data-rounded=full]) {
241
246
  --kit-sheet-radius: var(--kit-shape-full);
242
247
  }
243
248
 
@@ -245,22 +250,22 @@ button.kit-sheet {
245
250
  * density
246
251
  * @link https://lapikit.dev/docs/customize
247
252
  */
248
- .kit-sheet[data-density=dense] {
253
+ :where(.kit-sheet[data-density=dense]) {
249
254
  --kit-sheet-density-offset: var(--kit-density-dense);
250
255
  }
251
256
 
252
- .kit-sheet[data-density=compact] {
257
+ :where(.kit-sheet[data-density=compact]) {
253
258
  --kit-sheet-density-offset: var(--kit-density-compact);
254
259
  }
255
260
 
256
- .kit-sheet[data-density=default] {
261
+ :where(.kit-sheet[data-density=default]) {
257
262
  --kit-sheet-density-offset: var(--kit-density-default);
258
263
  }
259
264
 
260
- .kit-sheet[data-density=comfortable] {
265
+ :where(.kit-sheet[data-density=comfortable]) {
261
266
  --kit-sheet-density-offset: var(--kit-density-comfortable);
262
267
  }
263
268
 
264
- .kit-sheet[data-density=spacious] {
269
+ :where(.kit-sheet[data-density=spacious]) {
265
270
  --kit-sheet-density-offset: var(--kit-density-spacious);
266
271
  }</style>
@@ -1,4 +1,4 @@
1
1
  import type { ColorProps } from '../@types';
2
- export declare function resolveColor(color: string | null | undefined): string | undefined;
3
- export declare function resolveOnColor(color: string | null | undefined): string | undefined;
2
+ export declare function resolveColor(color: string | false | null | undefined): string | undefined;
3
+ export declare function resolveOnColor(color: string | false | null | undefined): string | undefined;
4
4
  export declare function colorStyle(name: string, color: ColorProps | null | undefined, active?: boolean): string;
@@ -2,7 +2,8 @@ import { DEV } from 'esm-env';
2
2
  const paletteName = /^[a-zA-Z][\w-]*$/;
3
3
  const unsafe = /[;{}]/;
4
4
  function clean(color) {
5
- const value = color?.trim();
5
+ // color={isActive && 'accent'} gives false: only a string is a color
6
+ const value = typeof color === 'string' ? color.trim() : undefined;
6
7
  if (!value)
7
8
  return undefined;
8
9
  if (unsafe.test(value)) {
@@ -1,4 +1,4 @@
1
- import type { ColorProps, ComponentAttrs, ElevationProps, ElevationState, PropValue, SStyleProp, useClassNameProps, useStylesProps } from '../@types';
1
+ import type { ColorProps, ComponentAttrs, ElevationProps, ElevationState, PropValue, SStyleProp, useStylesProps } from '../@types';
2
2
  import type { ClassValue } from 'svelte/elements';
3
3
  /**
4
4
  * The classes of the s-class_xxx directives (class:xxx on a kit tag):
@@ -9,15 +9,6 @@ import type { ClassValue } from 'svelte/elements';
9
9
  * - false, null, undefined and '' give nothing.
10
10
  */
11
11
  export declare function directiveClasses(classProps: Record<string, PropValue> | undefined): string[];
12
- /**
13
- * useClassName - Utility to compute class names for a component.
14
- * @param baseClass - The base class name for the component.
15
- * @param className - Additional class names: a string or an array of strings.
16
- * @param sClass - The s-class property which can be a string, array, or object.
17
- * @param classProps - An object containing s-class_xxx directives.
18
- * @returns A computed class string: no extra spaces, no duplicates, in the order of the inputs.
19
- */
20
- export declare function useClassName({ baseClass, className, sClass, classProps }?: useClassNameProps): string;
21
12
  export declare function useIsInteractive(props: Record<string, unknown>, tag: string, interactiveTags: readonly string[], interactive?: boolean): boolean;
22
13
  /**
23
14
  * useStyles - Utility to compute style declarations for a component (pure function).
@@ -58,3 +49,12 @@ export declare function useComponentAttrs(baseClass: string, { className, sClass
58
49
  color?: ColorProps | null;
59
50
  active?: boolean;
60
51
  }, rest: Record<string, unknown>): ComponentAttrs;
52
+ /**
53
+ * splitAriaAttrs - Separates the aria-* attributes from the other attributes, for a component whose
54
+ * role is on another element than its content (dialog, modal).
55
+ * @param rest - The rest of the props of the component.
56
+ */
57
+ export declare function splitAriaAttrs(rest: Record<string, unknown>): {
58
+ aria: Record<string, unknown>;
59
+ others: Record<string, unknown>;
60
+ };
@@ -3,8 +3,6 @@ import { makeComponentProps } from '../html-mapped';
3
3
  import { colorStyle } from './color.js';
4
4
  const endsWithSeparator = /[^a-zA-Z0-9]$/;
5
5
  const startsWithSeparator = /^[^a-zA-Z0-9]/;
6
- const whitespace = /\s/;
7
- const whitespaces = /\s+/;
8
6
  const propertyCache = new Map();
9
7
  function hasKeys(value) {
10
8
  if (!value || typeof value !== 'object')
@@ -15,39 +13,6 @@ function hasKeys(value) {
15
13
  }
16
14
  return false;
17
15
  }
18
- /**
19
- * Adds the classes of a value (one or several, separated by spaces) to the set.
20
- * The set keeps the first position of each class and drops the duplicates.
21
- */
22
- function addClasses(classes, value) {
23
- if (!whitespace.test(value)) {
24
- if (value)
25
- classes.add(value);
26
- return;
27
- }
28
- for (const name of value.split(whitespaces)) {
29
- if (name)
30
- classes.add(name);
31
- }
32
- }
33
- /**
34
- * Adds a class value with the rules of clsx (class on an element): a string, an array (nested too),
35
- * or an object whose keys are added when their value is truthy. Joining an array would give "a,b"
36
- */
37
- function addClassValue(classes, value) {
38
- if (typeof value === 'string')
39
- addClasses(classes, value);
40
- else if (Array.isArray(value)) {
41
- for (const item of value)
42
- addClassValue(classes, item);
43
- }
44
- else if (value && typeof value === 'object') {
45
- for (const key in value) {
46
- if (value[key])
47
- addClasses(classes, key);
48
- }
49
- }
50
- }
51
16
  /**
52
17
  * The classes of the s-class_xxx directives (class:xxx on a kit tag):
53
18
  * - true gives the name (class:active -> active);
@@ -74,46 +39,6 @@ export function directiveClasses(classProps) {
74
39
  }
75
40
  return classes;
76
41
  }
77
- /**
78
- * useClassName - Utility to compute class names for a component.
79
- * @param baseClass - The base class name for the component.
80
- * @param className - Additional class names: a string or an array of strings.
81
- * @param sClass - The s-class property which can be a string, array, or object.
82
- * @param classProps - An object containing s-class_xxx directives.
83
- * @returns A computed class string: no extra spaces, no duplicates, in the order of the inputs.
84
- */
85
- export function useClassName({ baseClass = '', className, sClass, classProps } = {}) {
86
- if (!sClass && !className && !hasKeys(classProps))
87
- return baseClass;
88
- const classes = new Set();
89
- if (baseClass)
90
- addClasses(classes, baseClass);
91
- if (typeof sClass === 'string') {
92
- addClasses(classes, sClass);
93
- }
94
- else if (Array.isArray(sClass)) {
95
- for (const value of sClass) {
96
- if (typeof value === 'string')
97
- addClasses(classes, value);
98
- }
99
- }
100
- else if (sClass && typeof sClass === 'object') {
101
- for (const key in sClass) {
102
- const value = sClass[key];
103
- if (value === true)
104
- addClasses(classes, key);
105
- else if (typeof value === 'string')
106
- addClasses(classes, value);
107
- }
108
- }
109
- if (classProps) {
110
- for (const name of directiveClasses(classProps))
111
- addClasses(classes, name);
112
- }
113
- if (className)
114
- addClassValue(classes, className);
115
- return [...classes].join(' ');
116
- }
117
42
  /**
118
43
  * useIsInteractive - Utility to determine whether a component should behave as interactive:
119
44
  * either explicitly flagged, rendered as an interactive tag (e.g. 'a', 'button'), or given
@@ -243,3 +168,19 @@ export function useComponentAttrs(baseClass, { className, sClass, styleAttr, sSt
243
168
  rest: restProps
244
169
  };
245
170
  }
171
+ /**
172
+ * splitAriaAttrs - Separates the aria-* attributes from the other attributes, for a component whose
173
+ * role is on another element than its content (dialog, modal).
174
+ * @param rest - The rest of the props of the component.
175
+ */
176
+ export function splitAriaAttrs(rest) {
177
+ const aria = {};
178
+ const others = {};
179
+ for (const key in rest) {
180
+ if (key.startsWith('aria-'))
181
+ aria[key] = rest[key];
182
+ else
183
+ others[key] = rest[key];
184
+ }
185
+ return { aria, others };
186
+ }
@@ -0,0 +1,58 @@
1
+ export type FloatingSide = 'top' | 'bottom' | 'left' | 'right';
2
+ export type FloatingAlign = 'start' | 'center' | 'end';
3
+ /** An element, or a point of the viewport (the position of a pointer event) */
4
+ export type FloatingAnchor = HTMLElement | {
5
+ x: number;
6
+ y: number;
7
+ };
8
+ export interface FloatingOptions {
9
+ anchor: FloatingAnchor | null | undefined;
10
+ /** false hides the element without removing it (default true) */
11
+ open?: boolean;
12
+ side?: FloatingSide;
13
+ align?: FloatingAlign;
14
+ /** space between the anchor and the floating element, in px */
15
+ offset?: number;
16
+ /** minimal space between the floating element and the edges of the viewport, in px */
17
+ padding?: number;
18
+ /** moves to the opposite side when there is not enough space */
19
+ flip?: boolean;
20
+ }
21
+ export interface FloatingRect {
22
+ top: number;
23
+ right: number;
24
+ bottom: number;
25
+ left: number;
26
+ width: number;
27
+ height: number;
28
+ }
29
+ export interface FloatingPosition {
30
+ x: number;
31
+ y: number;
32
+ side: FloatingSide;
33
+ /** position of the center of the anchor along the floating element, for an arrow */
34
+ arrow: number;
35
+ }
36
+ /**
37
+ * computeFloating - The position of a floating element next to an anchor (pure function).
38
+ * @param anchor - The rect of the anchor, in the viewport.
39
+ * @param size - The size of the floating element (offsetWidth / offsetHeight: not affected by a transform).
40
+ * @param viewport - The size of the viewport, without the scrollbars.
41
+ */
42
+ export declare function computeFloating(anchor: FloatingRect, size: {
43
+ width: number;
44
+ height: number;
45
+ }, viewport: {
46
+ width: number;
47
+ height: number;
48
+ }, { side, align, offset, padding, flip }?: Omit<FloatingOptions, 'anchor'>): FloatingPosition;
49
+ /**
50
+ * floating - Shows an element in the top layer (Popover API) next to its anchor, and keeps it there.
51
+ * The element needs the popover attribute. It gets its position (left / top), its side (data-side)
52
+ * and the position of the arrow (--kit-floating-arrow).
53
+ * It follows the scroll of every ancestor, the resize of the viewport and the size of both elements.
54
+ */
55
+ export declare function floating(node: HTMLElement, options: FloatingOptions): {
56
+ update(next: FloatingOptions): void;
57
+ destroy(): void;
58
+ };
@@ -0,0 +1,131 @@
1
+ const opposite = {
2
+ top: 'bottom',
3
+ bottom: 'top',
4
+ left: 'right',
5
+ right: 'left'
6
+ };
7
+ function clamp(value, min, max) {
8
+ return Math.min(Math.max(value, min), Math.max(min, max));
9
+ }
10
+ /**
11
+ * computeFloating - The position of a floating element next to an anchor (pure function).
12
+ * @param anchor - The rect of the anchor, in the viewport.
13
+ * @param size - The size of the floating element (offsetWidth / offsetHeight: not affected by a transform).
14
+ * @param viewport - The size of the viewport, without the scrollbars.
15
+ */
16
+ export function computeFloating(anchor, size, viewport, { side = 'bottom', align = 'start', offset = 6, padding = 8, flip = true } = {}) {
17
+ const space = {
18
+ top: anchor.top - offset - padding,
19
+ bottom: viewport.height - anchor.bottom - offset - padding,
20
+ left: anchor.left - offset - padding,
21
+ right: viewport.width - anchor.right - offset - padding
22
+ };
23
+ const vertical = side === 'top' || side === 'bottom';
24
+ const needed = vertical ? size.height : size.width;
25
+ let resolvedSide = side;
26
+ if (flip && space[side] < needed && space[opposite[side]] > space[side]) {
27
+ resolvedSide = opposite[side];
28
+ }
29
+ let x;
30
+ let y;
31
+ if (vertical) {
32
+ y = resolvedSide === 'top' ? anchor.top - offset - size.height : anchor.bottom + offset;
33
+ x =
34
+ align === 'start'
35
+ ? anchor.left
36
+ : align === 'end'
37
+ ? anchor.right - size.width
38
+ : anchor.left + (anchor.width - size.width) / 2;
39
+ }
40
+ else {
41
+ x = resolvedSide === 'left' ? anchor.left - offset - size.width : anchor.right + offset;
42
+ y =
43
+ align === 'start'
44
+ ? anchor.top
45
+ : align === 'end'
46
+ ? anchor.bottom - size.height
47
+ : anchor.top + (anchor.height - size.height) / 2;
48
+ }
49
+ // shift: the floating element stays in the viewport
50
+ x = clamp(x, padding, viewport.width - size.width - padding);
51
+ y = clamp(y, padding, viewport.height - size.height - padding);
52
+ const arrow = vertical
53
+ ? clamp(anchor.left + anchor.width / 2 - x, 8, size.width - 8)
54
+ : clamp(anchor.top + anchor.height / 2 - y, 8, size.height - 8);
55
+ return { x, y, side: resolvedSide, arrow };
56
+ }
57
+ function anchorRect(anchor) {
58
+ if (anchor instanceof HTMLElement)
59
+ return anchor.getBoundingClientRect();
60
+ return { top: anchor.y, right: anchor.x, bottom: anchor.y, left: anchor.x, width: 0, height: 0 };
61
+ }
62
+ function supportsPopover(node) {
63
+ return typeof node.showPopover === 'function';
64
+ }
65
+ /**
66
+ * floating - Shows an element in the top layer (Popover API) next to its anchor, and keeps it there.
67
+ * The element needs the popover attribute. It gets its position (left / top), its side (data-side)
68
+ * and the position of the arrow (--kit-floating-arrow).
69
+ * It follows the scroll of every ancestor, the resize of the viewport and the size of both elements.
70
+ */
71
+ export function floating(node, options) {
72
+ let frame = 0;
73
+ function toggle() {
74
+ if (!supportsPopover(node))
75
+ return;
76
+ const shown = node.matches(':popover-open');
77
+ if (options.open !== false && !shown)
78
+ node.showPopover();
79
+ if (options.open === false && shown)
80
+ node.hidePopover();
81
+ }
82
+ function update() {
83
+ const { anchor, open, ...rest } = options;
84
+ if (open === false || !anchor || !node.isConnected)
85
+ return;
86
+ if (anchor instanceof HTMLElement && !anchor.isConnected)
87
+ return;
88
+ const position = computeFloating(anchorRect(anchor), { width: node.offsetWidth, height: node.offsetHeight }, {
89
+ width: document.documentElement.clientWidth,
90
+ height: document.documentElement.clientHeight
91
+ }, rest);
92
+ node.style.left = `${position.x}px`;
93
+ node.style.top = `${position.y}px`;
94
+ node.style.setProperty('--kit-floating-arrow', `${position.arrow}px`);
95
+ node.dataset.side = position.side;
96
+ }
97
+ function schedule() {
98
+ cancelAnimationFrame(frame);
99
+ frame = requestAnimationFrame(update);
100
+ }
101
+ toggle();
102
+ update();
103
+ // capture: the scroll of any ancestor, not only the window
104
+ window.addEventListener('scroll', schedule, { capture: true, passive: true });
105
+ window.addEventListener('resize', schedule, { passive: true });
106
+ const observer = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(schedule);
107
+ observer?.observe(node);
108
+ if (options.anchor instanceof HTMLElement)
109
+ observer?.observe(options.anchor);
110
+ return {
111
+ update(next) {
112
+ if (next.anchor !== options.anchor) {
113
+ if (options.anchor instanceof HTMLElement)
114
+ observer?.unobserve(options.anchor);
115
+ if (next.anchor instanceof HTMLElement)
116
+ observer?.observe(next.anchor);
117
+ }
118
+ options = next;
119
+ toggle();
120
+ schedule();
121
+ },
122
+ destroy() {
123
+ cancelAnimationFrame(frame);
124
+ window.removeEventListener('scroll', schedule, { capture: true });
125
+ window.removeEventListener('resize', schedule);
126
+ observer?.disconnect();
127
+ if (supportsPopover(node) && node.matches(':popover-open'))
128
+ node.hidePopover();
129
+ }
130
+ };
131
+ }
@@ -1,3 +1,4 @@
1
1
  export * from './components.js';
2
2
  export * from './outside.js';
3
3
  export * from './color.js';
4
+ export * from './floating.js';
@@ -1,3 +1,4 @@
1
1
  export * from './components.js';
2
2
  export * from './outside.js';
3
3
  export * from './color.js';
4
+ export * from './floating.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lapikit",
3
- "version": "0.7.3",
3
+ "version": "0.7.4",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -81,7 +81,7 @@
81
81
  }
82
82
  },
83
83
  "peerDependencies": {
84
- "svelte": "^5.0.0"
84
+ "svelte": "^5.20.0"
85
85
  },
86
86
  "devDependencies": {
87
87
  "@eslint/compat": "^1.2.5",
@@ -106,7 +106,7 @@
106
106
  "prettier-plugin-svelte": "^3.3.3",
107
107
  "publint": "^0.3.2",
108
108
  "sass": "^1.105.1",
109
- "svelte": "^5.0.0",
109
+ "svelte": "^5.20.0",
110
110
  "svelte-check": "^4.0.0",
111
111
  "typescript": "^5.0.0",
112
112
  "typescript-eslint": "^8.20.0",