compote-ui 0.79.1 → 0.80.0

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 (69) hide show
  1. package/dist/components/data-table-v9/column-filter-editing.svelte.d.ts +41 -0
  2. package/dist/components/data-table-v9/column-filter-editing.svelte.js +88 -0
  3. package/dist/components/data-table-v9/column-filter-kind.d.ts +10 -0
  4. package/dist/components/data-table-v9/column-filter-kind.js +25 -0
  5. package/dist/components/data-table-v9/create-table.svelte.js +49 -26
  6. package/dist/components/data-table-v9/data-table-head.svelte +230 -201
  7. package/dist/components/data-table-v9/data-table-head.svelte.d.ts +2 -0
  8. package/dist/components/data-table-v9/data-table-utils.d.ts +7 -0
  9. package/dist/components/data-table-v9/data-table-utils.js +4 -0
  10. package/dist/components/data-table-v9/data-table.svelte +154 -151
  11. package/dist/components/data-table-v9/data-table.svelte.d.ts +2 -0
  12. package/dist/components/data-table-v9/date-cell.d.ts +13 -0
  13. package/dist/components/data-table-v9/date-cell.js +38 -0
  14. package/dist/components/data-table-v9/filter-editors/boolean-filter-editor.svelte +45 -0
  15. package/dist/components/data-table-v9/filter-editors/boolean-filter-editor.svelte.d.ts +7 -0
  16. package/dist/components/data-table-v9/filter-editors/column-filter-editor.svelte +33 -0
  17. package/dist/components/data-table-v9/filter-editors/column-filter-editor.svelte.d.ts +30 -0
  18. package/dist/components/data-table-v9/filter-editors/date-range-filter-editor.svelte +52 -0
  19. package/dist/components/data-table-v9/filter-editors/date-range-filter-editor.svelte.d.ts +28 -0
  20. package/dist/components/data-table-v9/filter-editors/range-filter-editor.svelte +51 -0
  21. package/dist/components/data-table-v9/filter-editors/range-filter-editor.svelte.d.ts +30 -0
  22. package/dist/components/data-table-v9/filter-editors/select-filter-editor.svelte +59 -0
  23. package/dist/components/data-table-v9/filter-editors/select-filter-editor.svelte.d.ts +28 -0
  24. package/dist/components/data-table-v9/filter-editors/text-filter-editor.svelte +20 -0
  25. package/dist/components/data-table-v9/filter-editors/text-filter-editor.svelte.d.ts +8 -0
  26. package/dist/components/data-table-v9/filter-revisions.svelte.d.ts +17 -0
  27. package/dist/components/data-table-v9/filter-revisions.svelte.js +42 -0
  28. package/dist/components/data-table-v9/header/column-header-filter.svelte +66 -0
  29. package/dist/components/data-table-v9/header/column-header-filter.svelte.d.ts +30 -0
  30. package/dist/components/data-table-v9/table-view-state.svelte.d.ts +1 -0
  31. package/dist/components/data-table-v9/table-view-state.svelte.js +3 -0
  32. package/dist/components/data-table-v9/toolbar/data-table-column-filter.svelte +203 -433
  33. package/dist/components/data-table-v9/toolbar/data-table-column-filter.svelte.d.ts +1 -1
  34. package/dist/components/data-table-v9/virtual/data-table-virtualized.svelte +82 -79
  35. package/dist/components/data-table-v9/virtual/data-table-virtualized.svelte.d.ts +2 -0
  36. package/dist/components/dialog/alert-dialog.svelte +73 -71
  37. package/dist/components/dialog/dialog-root.svelte +45 -43
  38. package/dist/components/drawer/drawer-content.svelte +213 -211
  39. package/dist/components/number-input/number-input.svelte +2 -6
  40. package/dist/components/popover/popover-content.svelte +97 -71
  41. package/dist/components/popover/popover-content.svelte.d.ts +2 -1
  42. package/dist/components/popover/popover-description.svelte +19 -15
  43. package/dist/components/popover/popover-labels.svelte.d.ts +18 -0
  44. package/dist/components/popover/popover-labels.svelte.js +35 -0
  45. package/dist/components/popover/popover-root.svelte +27 -24
  46. package/dist/components/popover/popover-title.svelte +19 -15
  47. package/dist/icons/PhFunnelFill.svelte +19 -0
  48. package/dist/icons/PhFunnelFill.svelte.d.ts +5 -0
  49. package/dist/icons/index.d.ts +1 -0
  50. package/dist/icons/index.js +1 -0
  51. package/dist/utils/date-filter.d.ts +31 -0
  52. package/dist/utils/date-filter.js +58 -0
  53. package/dist/utils/date.d.ts +19 -1
  54. package/dist/utils/date.js +48 -1
  55. package/dist/utils/debounced-filter-edit.svelte.d.ts +16 -0
  56. package/dist/utils/debounced-filter-edit.svelte.js +56 -0
  57. package/dist/utils/number-input-value.d.ts +10 -0
  58. package/dist/utils/number-input-value.js +15 -0
  59. package/dist/utils/range-filter.d.ts +19 -0
  60. package/dist/utils/range-filter.js +23 -0
  61. package/dist/utils/release-focus-on-exit.d.ts +22 -0
  62. package/dist/utils/release-focus-on-exit.js +26 -0
  63. package/package.json +1 -1
  64. package/skills/component-usage/SKILL.md +1 -1
  65. package/skills/component-usage/references/forms-and-inputs.md +3 -0
  66. package/skills/component-usage/references/overlays-and-floating-ui.md +278 -273
  67. package/skills/data-table/SKILL.md +314 -283
  68. package/skills/data-table/references/column-options.md +175 -150
  69. package/skills/data-table/references/table-state-and-reactivity.md +65 -45
@@ -1,211 +1,213 @@
1
- <script lang="ts">
2
- import { Drawer } from '@ark-ui/svelte/drawer';
3
- import type { DrawerContentBaseProps } from '@ark-ui/svelte/drawer';
4
- import type { ClassValue } from 'svelte/elements';
5
- import { cn } from 'tailwind-variants';
6
-
7
- interface Props extends DrawerContentBaseProps {
8
- class?: ClassValue;
9
- backdropClass?: ClassValue;
10
- positionerClass?: ClassValue;
11
- maxWidth?: string;
12
- maxHeight?: string;
13
- }
14
-
15
- let {
16
- class: className,
17
- backdropClass,
18
- positionerClass,
19
- maxWidth,
20
- maxHeight,
21
- children,
22
- draggable = false,
23
- ...rest
24
- }: Props = $props();
25
- </script>
26
-
27
- <Drawer.Backdrop
28
- class={cn(
29
- 'fixed inset-0 z-50 bg-black/50',
30
- 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0',
31
- 'data-[state=open]:animate-in data-[state=open]:fade-in-0',
32
- backdropClass
33
- )}
34
- />
35
- <Drawer.Positioner
36
- class={cn(
37
- 'fixed inset-0 z-50 flex items-end justify-center',
38
- 'data-[swipe-direction=left]:items-stretch data-[swipe-direction=left]:justify-start',
39
- 'data-[swipe-direction=right]:items-stretch data-[swipe-direction=right]:justify-end',
40
- 'data-[swipe-direction=up]:items-start',
41
- positionerClass
42
- )}
43
- >
44
- <Drawer.Content
45
- {...rest}
46
- {draggable}
47
- style={[
48
- maxWidth && `--drawer-max-width:${maxWidth}`,
49
- maxHeight && `--drawer-max-height:${maxHeight}`
50
- ]
51
- .filter(Boolean)
52
- .join(';')}
53
- class={cn(
54
- 'pointer-events-auto relative flex h-full w-full flex-col bg-surface-1 shadow-xl outline-none',
55
- className
56
- )}
57
- >
58
- {@render children?.()}
59
- </Drawer.Content>
60
- </Drawer.Positioner>
61
-
62
- <style>
63
- /* Side drawers: grabber becomes an absolutely-positioned vertical strip on the open edge */
64
- :global(
65
- [data-scope='drawer'][data-part='content'][data-swipe-direction='left']
66
- [data-scope='drawer'][data-part='grabber'],
67
- [data-scope='drawer'][data-part='content'][data-swipe-direction='right']
68
- [data-scope='drawer'][data-part='grabber']
69
- ) {
70
- position: absolute;
71
- top: 0;
72
- bottom: 0;
73
- width: auto;
74
- height: 100%;
75
- padding: 0 0.75rem;
76
- }
77
-
78
- :global(
79
- [data-scope='drawer'][data-part='content'][data-swipe-direction='left']
80
- [data-scope='drawer'][data-part='grabber']
81
- ) {
82
- right: 0;
83
- }
84
-
85
- :global(
86
- [data-scope='drawer'][data-part='content'][data-swipe-direction='right']
87
- [data-scope='drawer'][data-part='grabber']
88
- ) {
89
- left: 0;
90
- }
91
-
92
- :global(
93
- [data-scope='drawer'][data-part='content'][data-swipe-direction='left']
94
- [data-scope='drawer'][data-part='grabber-indicator'],
95
- [data-scope='drawer'][data-part='content'][data-swipe-direction='right']
96
- [data-scope='drawer'][data-part='grabber-indicator']
97
- ) {
98
- width: 4px;
99
- height: 2.5rem;
100
- }
101
-
102
- /* Bottom/top drawers: cap height, overridable via --drawer-max-height */
103
- :global(
104
- [data-scope='drawer'][data-part='content']:not([data-swipe-direction]),
105
- [data-scope='drawer'][data-part='content'][data-swipe-direction='down'],
106
- [data-scope='drawer'][data-part='content'][data-swipe-direction='up']
107
- ) {
108
- max-height: var(--drawer-max-height, 96svh);
109
- }
110
-
111
- /* Side drawers: constrain width, remove height cap — overridable via --drawer-max-width */
112
- :global(
113
- [data-scope='drawer'][data-part='content'][data-swipe-direction='left'],
114
- [data-scope='drawer'][data-part='content'][data-swipe-direction='right']
115
- ) {
116
- max-width: var(--drawer-max-width, 25rem);
117
- max-height: none;
118
- }
119
-
120
- /* Bleed pseudo-element so background extends past rounded corners on the open edge */
121
- :global([data-scope='drawer'][data-part='content'])::after {
122
- content: '';
123
- position: absolute;
124
- left: 0;
125
- right: 0;
126
- top: 100%;
127
- height: 50px;
128
- background: inherit;
129
- pointer-events: none;
130
- }
131
-
132
- :global([data-scope='drawer'][data-part='content'][data-swipe-direction='up'])::after {
133
- top: auto;
134
- bottom: 100%;
135
- }
136
-
137
- :global([data-scope='drawer'][data-part='content'][data-swipe-direction='left'])::after {
138
- top: 0;
139
- bottom: 0;
140
- left: auto;
141
- right: 100%;
142
- width: 50px;
143
- height: auto;
144
- }
145
-
146
- :global([data-scope='drawer'][data-part='content'][data-swipe-direction='right'])::after {
147
- top: 0;
148
- bottom: 0;
149
- right: auto;
150
- left: 100%;
151
- width: 50px;
152
- height: auto;
153
- }
154
-
155
- :global([data-scope='drawer'][data-part='content'][data-state='open']) {
156
- animation-name: enter;
157
- animation-duration: 500ms;
158
- animation-fill-mode: both;
159
- }
160
- :global([data-scope='drawer'][data-part='content'][data-state='closed']) {
161
- animation-name: exit;
162
- animation-duration: 300ms;
163
- animation-fill-mode: both;
164
- }
165
-
166
- :global(
167
- [data-scope='drawer'][data-part='content']:not([data-swipe-direction])[data-state='open'],
168
- [data-scope='drawer'][data-part='content'][data-swipe-direction='down'][data-state='open']
169
- ) {
170
- --tw-enter-translate-y: 100%;
171
- }
172
- :global(
173
- [data-scope='drawer'][data-part='content']:not([data-swipe-direction])[data-state='closed'],
174
- [data-scope='drawer'][data-part='content'][data-swipe-direction='down'][data-state='closed']
175
- ) {
176
- --tw-exit-translate-y: 100%;
177
- }
178
-
179
- :global(
180
- [data-scope='drawer'][data-part='content'][data-swipe-direction='up'][data-state='open']
181
- ) {
182
- --tw-enter-translate-y: -100%;
183
- }
184
- :global(
185
- [data-scope='drawer'][data-part='content'][data-swipe-direction='up'][data-state='closed']
186
- ) {
187
- --tw-exit-translate-y: -100%;
188
- }
189
-
190
- :global(
191
- [data-scope='drawer'][data-part='content'][data-swipe-direction='left'][data-state='open']
192
- ) {
193
- --tw-enter-translate-x: -100%;
194
- }
195
- :global(
196
- [data-scope='drawer'][data-part='content'][data-swipe-direction='left'][data-state='closed']
197
- ) {
198
- --tw-exit-translate-x: -100%;
199
- }
200
-
201
- :global(
202
- [data-scope='drawer'][data-part='content'][data-swipe-direction='right'][data-state='open']
203
- ) {
204
- --tw-enter-translate-x: 100%;
205
- }
206
- :global(
207
- [data-scope='drawer'][data-part='content'][data-swipe-direction='right'][data-state='closed']
208
- ) {
209
- --tw-exit-translate-x: 100%;
210
- }
211
- </style>
1
+ <script lang="ts">
2
+ import { Drawer } from '@ark-ui/svelte/drawer';
3
+ import type { DrawerContentBaseProps } from '@ark-ui/svelte/drawer';
4
+ import type { ClassValue } from 'svelte/elements';
5
+ import { cn } from 'tailwind-variants';
6
+ import { releaseFocusOnExitProps } from '../../utils/release-focus-on-exit';
7
+
8
+ interface Props extends DrawerContentBaseProps {
9
+ class?: ClassValue;
10
+ backdropClass?: ClassValue;
11
+ positionerClass?: ClassValue;
12
+ maxWidth?: string;
13
+ maxHeight?: string;
14
+ }
15
+
16
+ let {
17
+ class: className,
18
+ backdropClass,
19
+ positionerClass,
20
+ maxWidth,
21
+ maxHeight,
22
+ children,
23
+ draggable = false,
24
+ ...rest
25
+ }: Props = $props();
26
+ </script>
27
+
28
+ <Drawer.Backdrop
29
+ class={cn(
30
+ 'fixed inset-0 z-50 bg-black/50',
31
+ 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0',
32
+ 'data-[state=open]:animate-in data-[state=open]:fade-in-0',
33
+ backdropClass
34
+ )}
35
+ />
36
+ <Drawer.Positioner
37
+ class={cn(
38
+ 'fixed inset-0 z-50 flex items-end justify-center',
39
+ 'data-[swipe-direction=left]:items-stretch data-[swipe-direction=left]:justify-start',
40
+ 'data-[swipe-direction=right]:items-stretch data-[swipe-direction=right]:justify-end',
41
+ 'data-[swipe-direction=up]:items-start',
42
+ positionerClass
43
+ )}
44
+ >
45
+ <Drawer.Content
46
+ {...rest}
47
+ {draggable}
48
+ style={[
49
+ maxWidth && `--drawer-max-width:${maxWidth}`,
50
+ maxHeight && `--drawer-max-height:${maxHeight}`
51
+ ]
52
+ .filter(Boolean)
53
+ .join(';')}
54
+ class={cn(
55
+ 'pointer-events-auto relative flex h-full w-full flex-col bg-surface-1 shadow-xl outline-none',
56
+ className
57
+ )}
58
+ {...releaseFocusOnExitProps}
59
+ >
60
+ {@render children?.()}
61
+ </Drawer.Content>
62
+ </Drawer.Positioner>
63
+
64
+ <style>
65
+ /* Side drawers: grabber becomes an absolutely-positioned vertical strip on the open edge */
66
+ :global(
67
+ [data-scope='drawer'][data-part='content'][data-swipe-direction='left']
68
+ [data-scope='drawer'][data-part='grabber'],
69
+ [data-scope='drawer'][data-part='content'][data-swipe-direction='right']
70
+ [data-scope='drawer'][data-part='grabber']
71
+ ) {
72
+ position: absolute;
73
+ top: 0;
74
+ bottom: 0;
75
+ width: auto;
76
+ height: 100%;
77
+ padding: 0 0.75rem;
78
+ }
79
+
80
+ :global(
81
+ [data-scope='drawer'][data-part='content'][data-swipe-direction='left']
82
+ [data-scope='drawer'][data-part='grabber']
83
+ ) {
84
+ right: 0;
85
+ }
86
+
87
+ :global(
88
+ [data-scope='drawer'][data-part='content'][data-swipe-direction='right']
89
+ [data-scope='drawer'][data-part='grabber']
90
+ ) {
91
+ left: 0;
92
+ }
93
+
94
+ :global(
95
+ [data-scope='drawer'][data-part='content'][data-swipe-direction='left']
96
+ [data-scope='drawer'][data-part='grabber-indicator'],
97
+ [data-scope='drawer'][data-part='content'][data-swipe-direction='right']
98
+ [data-scope='drawer'][data-part='grabber-indicator']
99
+ ) {
100
+ width: 4px;
101
+ height: 2.5rem;
102
+ }
103
+
104
+ /* Bottom/top drawers: cap height, overridable via --drawer-max-height */
105
+ :global(
106
+ [data-scope='drawer'][data-part='content']:not([data-swipe-direction]),
107
+ [data-scope='drawer'][data-part='content'][data-swipe-direction='down'],
108
+ [data-scope='drawer'][data-part='content'][data-swipe-direction='up']
109
+ ) {
110
+ max-height: var(--drawer-max-height, 96svh);
111
+ }
112
+
113
+ /* Side drawers: constrain width, remove height cap — overridable via --drawer-max-width */
114
+ :global(
115
+ [data-scope='drawer'][data-part='content'][data-swipe-direction='left'],
116
+ [data-scope='drawer'][data-part='content'][data-swipe-direction='right']
117
+ ) {
118
+ max-width: var(--drawer-max-width, 25rem);
119
+ max-height: none;
120
+ }
121
+
122
+ /* Bleed pseudo-element so background extends past rounded corners on the open edge */
123
+ :global([data-scope='drawer'][data-part='content'])::after {
124
+ content: '';
125
+ position: absolute;
126
+ left: 0;
127
+ right: 0;
128
+ top: 100%;
129
+ height: 50px;
130
+ background: inherit;
131
+ pointer-events: none;
132
+ }
133
+
134
+ :global([data-scope='drawer'][data-part='content'][data-swipe-direction='up'])::after {
135
+ top: auto;
136
+ bottom: 100%;
137
+ }
138
+
139
+ :global([data-scope='drawer'][data-part='content'][data-swipe-direction='left'])::after {
140
+ top: 0;
141
+ bottom: 0;
142
+ left: auto;
143
+ right: 100%;
144
+ width: 50px;
145
+ height: auto;
146
+ }
147
+
148
+ :global([data-scope='drawer'][data-part='content'][data-swipe-direction='right'])::after {
149
+ top: 0;
150
+ bottom: 0;
151
+ right: auto;
152
+ left: 100%;
153
+ width: 50px;
154
+ height: auto;
155
+ }
156
+
157
+ :global([data-scope='drawer'][data-part='content'][data-state='open']) {
158
+ animation-name: enter;
159
+ animation-duration: 500ms;
160
+ animation-fill-mode: both;
161
+ }
162
+ :global([data-scope='drawer'][data-part='content'][data-state='closed']) {
163
+ animation-name: exit;
164
+ animation-duration: 300ms;
165
+ animation-fill-mode: both;
166
+ }
167
+
168
+ :global(
169
+ [data-scope='drawer'][data-part='content']:not([data-swipe-direction])[data-state='open'],
170
+ [data-scope='drawer'][data-part='content'][data-swipe-direction='down'][data-state='open']
171
+ ) {
172
+ --tw-enter-translate-y: 100%;
173
+ }
174
+ :global(
175
+ [data-scope='drawer'][data-part='content']:not([data-swipe-direction])[data-state='closed'],
176
+ [data-scope='drawer'][data-part='content'][data-swipe-direction='down'][data-state='closed']
177
+ ) {
178
+ --tw-exit-translate-y: 100%;
179
+ }
180
+
181
+ :global(
182
+ [data-scope='drawer'][data-part='content'][data-swipe-direction='up'][data-state='open']
183
+ ) {
184
+ --tw-enter-translate-y: -100%;
185
+ }
186
+ :global(
187
+ [data-scope='drawer'][data-part='content'][data-swipe-direction='up'][data-state='closed']
188
+ ) {
189
+ --tw-exit-translate-y: -100%;
190
+ }
191
+
192
+ :global(
193
+ [data-scope='drawer'][data-part='content'][data-swipe-direction='left'][data-state='open']
194
+ ) {
195
+ --tw-enter-translate-x: -100%;
196
+ }
197
+ :global(
198
+ [data-scope='drawer'][data-part='content'][data-swipe-direction='left'][data-state='closed']
199
+ ) {
200
+ --tw-exit-translate-x: -100%;
201
+ }
202
+
203
+ :global(
204
+ [data-scope='drawer'][data-part='content'][data-swipe-direction='right'][data-state='open']
205
+ ) {
206
+ --tw-enter-translate-x: 100%;
207
+ }
208
+ :global(
209
+ [data-scope='drawer'][data-part='content'][data-swipe-direction='right'][data-state='closed']
210
+ ) {
211
+ --tw-exit-translate-x: 100%;
212
+ }
213
+ </style>
@@ -4,6 +4,7 @@
4
4
  import type { NumberInputProps } from './types';
5
5
  import { PhCaretDown, PhCaretUp } from '../../icons';
6
6
  import { useLocaleContext } from '@ark-ui/svelte/locale';
7
+ import { toNumberInputValue } from '../../utils/number-input-value';
7
8
 
8
9
  let {
9
10
  value = $bindable(),
@@ -28,12 +29,7 @@
28
29
  {...restProps}
29
30
  allowMouseWheel
30
31
  locale={locale().locale}
31
- value={value != null
32
- ? new Intl.NumberFormat(locale().locale, {
33
- useGrouping: false,
34
- maximumFractionDigits: 20
35
- }).format(value)
36
- : undefined}
32
+ value={toNumberInputValue(value, locale().locale, restProps.defaultValue !== undefined)}
37
33
  readOnly={readonly}
38
34
  onValueChange={(valueChangeDetails) => {
39
35
  onValueChange?.(valueChangeDetails);
@@ -1,71 +1,97 @@
1
- <script lang="ts">
2
- import { Popover } from '@ark-ui/svelte/popover';
3
- import { Portal } from '@ark-ui/svelte/portal';
4
- import { cn } from 'tailwind-variants';
5
- import type { ClassValue } from 'svelte/elements';
6
- import type { Snippet } from 'svelte';
7
-
8
- interface Props {
9
- class?: ClassValue;
10
- children: Snippet;
11
- showArrow?: boolean;
12
- }
13
-
14
- let { class: className, children, showArrow = true }: Props = $props();
15
- </script>
16
-
17
- <Portal>
18
- <Popover.Positioner>
19
- <Popover.Content
20
- class={cn(
21
- 'z-50 w-72 rounded-md border bg-surface-document p-4 shadow-md outline-none [--arrow-background:var(--compote-surface-1)] [--arrow-size:10px]',
22
- 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
23
- 'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
24
- className
25
- )}
26
- >
27
- {#if showArrow}
28
- <Popover.Arrow>
29
- <Popover.ArrowTip class="border-t border-l border-border" />
30
- </Popover.Arrow>
31
- {/if}
32
- {@render children()}
33
- </Popover.Content>
34
- </Popover.Positioner>
35
- </Portal>
36
-
37
- <style>
38
- :global([data-scope='popover'][data-part='content']) {
39
- transform-origin: var(--transform-origin);
40
- }
41
-
42
- :global([data-scope='popover'][data-part='content'][data-state='open']) {
43
- animation: popover-in 150ms ease-out;
44
- }
45
-
46
- :global([data-scope='popover'][data-part='content'][data-state='closed']) {
47
- animation: popover-out 100ms ease-in;
48
- }
49
-
50
- @keyframes popover-in {
51
- from {
52
- opacity: 0;
53
- transform: scale(0.95);
54
- }
55
- to {
56
- opacity: 1;
57
- transform: scale(1);
58
- }
59
- }
60
-
61
- @keyframes popover-out {
62
- from {
63
- opacity: 1;
64
- transform: scale(1);
65
- }
66
- to {
67
- opacity: 0;
68
- transform: scale(0.95);
69
- }
70
- }
71
- </style>
1
+ <script lang="ts">
2
+ import { Popover, usePopoverContext } from '@ark-ui/svelte/popover';
3
+ import type { PopoverContentProps } from '@ark-ui/svelte/popover';
4
+ import { Portal } from '@ark-ui/svelte/portal';
5
+ import { cn } from 'tailwind-variants';
6
+ import { releaseFocusOnExitProps } from '../../utils/release-focus-on-exit';
7
+ import type { ClassValue } from 'svelte/elements';
8
+ import type { Snippet } from 'svelte';
9
+ import { getPopoverLabels } from './popover-labels.svelte';
10
+
11
+ interface Props extends Omit<PopoverContentProps, 'class' | 'children'> {
12
+ class?: ClassValue;
13
+ children: Snippet;
14
+ showArrow?: boolean;
15
+ }
16
+
17
+ let {
18
+ class: className,
19
+ children,
20
+ showArrow = true,
21
+ 'aria-labelledby': ariaLabelledby,
22
+ 'aria-describedby': ariaDescribedby,
23
+ ...rest
24
+ }: Props = $props();
25
+
26
+ const popover = usePopoverContext();
27
+ const labels = getPopoverLabels();
28
+
29
+ // Explicit labelling wins; otherwise link the Title / Description rendered inside.
30
+ const labelledby = $derived(
31
+ ariaLabelledby ??
32
+ (labels?.hasTitle && !rest['aria-label'] ? popover().getTitleProps().id : undefined)
33
+ );
34
+ const describedby = $derived(
35
+ ariaDescribedby ?? (labels?.hasDescription ? popover().getDescriptionProps().id : undefined)
36
+ );
37
+ </script>
38
+
39
+ <Portal>
40
+ <Popover.Positioner>
41
+ <Popover.Content
42
+ {...rest}
43
+ aria-labelledby={labelledby}
44
+ aria-describedby={describedby}
45
+ class={cn(
46
+ 'z-50 w-72 rounded-md border bg-surface-document p-4 shadow-md outline-none [--arrow-background:var(--compote-surface-1)] [--arrow-size:10px]',
47
+ 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
48
+ 'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
49
+ className
50
+ )}
51
+ {...releaseFocusOnExitProps}
52
+ >
53
+ {#if showArrow}
54
+ <Popover.Arrow>
55
+ <Popover.ArrowTip class="border-t border-l border-border" />
56
+ </Popover.Arrow>
57
+ {/if}
58
+ {@render children()}
59
+ </Popover.Content>
60
+ </Popover.Positioner>
61
+ </Portal>
62
+
63
+ <style>
64
+ :global([data-scope='popover'][data-part='content']) {
65
+ transform-origin: var(--transform-origin);
66
+ }
67
+
68
+ :global([data-scope='popover'][data-part='content'][data-state='open']) {
69
+ animation: popover-in 150ms ease-out;
70
+ }
71
+
72
+ :global([data-scope='popover'][data-part='content'][data-state='closed']) {
73
+ animation: popover-out 100ms ease-in;
74
+ }
75
+
76
+ @keyframes popover-in {
77
+ from {
78
+ opacity: 0;
79
+ transform: scale(0.95);
80
+ }
81
+ to {
82
+ opacity: 1;
83
+ transform: scale(1);
84
+ }
85
+ }
86
+
87
+ @keyframes popover-out {
88
+ from {
89
+ opacity: 1;
90
+ transform: scale(1);
91
+ }
92
+ to {
93
+ opacity: 0;
94
+ transform: scale(0.95);
95
+ }
96
+ }
97
+ </style>
@@ -1,6 +1,7 @@
1
+ import type { PopoverContentProps } from '@ark-ui/svelte/popover';
1
2
  import type { ClassValue } from 'svelte/elements';
2
3
  import type { Snippet } from 'svelte';
3
- interface Props {
4
+ interface Props extends Omit<PopoverContentProps, 'class' | 'children'> {
4
5
  class?: ClassValue;
5
6
  children: Snippet;
6
7
  showArrow?: boolean;