sheer-ui 0.4.0 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/lib/components/accordion/components/accordion-item.svelte +3 -3
- package/src/lib/components/accordion/components/accordion.svelte +29 -13
- package/src/lib/components/accordion/types.ts +3 -3
- package/src/lib/components/avatar/avatar.svelte.ts +34 -37
- package/src/lib/components/avatar/components/avatar-fallback.svelte +9 -6
- package/src/lib/components/avatar/components/avatar-image.svelte +18 -9
- package/src/lib/components/avatar/components/avatar.svelte +20 -16
- package/src/lib/components/checkbox/checkbox.svelte.ts +45 -55
- package/src/lib/components/checkbox/components/checkbox-group-label.svelte +9 -6
- package/src/lib/components/checkbox/components/checkbox-group.svelte +25 -14
- package/src/lib/components/checkbox/components/checkbox.svelte +39 -21
- package/src/lib/components/combobox/components/combobox.svelte +2 -2
- package/src/lib/components/combobox/select/components/select.svelte +2 -2
- package/src/lib/components/context-menu/components/context-menu-trigger.svelte +1 -1
- package/src/lib/components/date-field/date-field.svelte.ts +2 -3
- package/src/lib/components/dialog/components/dialog-close.svelte +13 -8
- package/src/lib/components/dialog/components/dialog-content-headless.svelte +14 -11
- package/src/lib/components/dialog/components/dialog-description.svelte +9 -6
- package/src/lib/components/dialog/components/dialog-overlay.svelte +10 -7
- package/src/lib/components/dialog/components/dialog-title.svelte +12 -7
- package/src/lib/components/dialog/components/dialog-trigger.svelte +12 -7
- package/src/lib/components/dialog/components/dialog.svelte +10 -4
- package/src/lib/components/dialog/components/modal-surface.svelte +11 -8
- package/src/lib/components/dialog/dialog.svelte.ts +73 -61
- package/src/lib/components/drawer/drawer.svelte +8 -3
- package/src/lib/components/link-preview/components/link-preview-content.svelte +15 -8
- package/src/lib/components/link-preview/components/link-preview-trigger.svelte +9 -6
- package/src/lib/components/link-preview/components/link-preview.svelte +19 -11
- package/src/lib/components/link-preview/link-preview.svelte.ts +42 -43
- package/src/lib/components/menu/components/menu-checkbox-group.svelte +6 -4
- package/src/lib/components/menu/components/menu-checkbox-item.svelte +11 -7
- package/src/lib/components/menu/menu.svelte.ts +29 -19
- package/src/lib/components/menubar/menubar.svelte.ts +7 -6
- package/src/lib/components/meter/components/meter.svelte +18 -9
- package/src/lib/components/meter/meter.svelte.ts +15 -18
- package/src/lib/components/navigation-menu/components/navigation-menu-content.svelte +1 -1
- package/src/lib/components/navigation-menu/components/navigation-menu-indicator.svelte +2 -2
- package/src/lib/components/navigation-menu/components/navigation-menu-trigger.svelte +1 -1
- package/src/lib/components/navigation-menu/navigation-menu.svelte.ts +57 -60
- package/src/lib/components/pagination/components/pagination-next-button.svelte +12 -7
- package/src/lib/components/pagination/components/pagination-page.svelte +15 -8
- package/src/lib/components/pagination/components/pagination-prev-button.svelte +12 -7
- package/src/lib/components/pagination/components/pagination.svelte +31 -18
- package/src/lib/components/pagination/pagination.svelte.ts +48 -64
- package/src/lib/components/pin-input/pin-input.svelte.ts +7 -8
- package/src/lib/components/pin-input/usePasswordManager.svelte.ts +2 -2
- package/src/lib/components/popover/components/popover-close.svelte +9 -6
- package/src/lib/components/popover/components/popover-content.svelte +12 -7
- package/src/lib/components/popover/components/popover-trigger.svelte +21 -10
- package/src/lib/components/popover/components/popover.svelte +9 -8
- package/src/lib/components/popover/popover.svelte.ts +68 -74
- package/src/lib/components/progress/components/progress.svelte +18 -9
- package/src/lib/components/progress/progress.svelte.ts +14 -17
- package/src/lib/components/scroll-area/scroll-area.svelte.ts +9 -10
- package/src/lib/components/separator/components/separator.svelte +15 -8
- package/src/lib/components/separator/separator.svelte.ts +12 -15
- package/src/lib/components/sidebar/sidebar-mobile-surface.svelte +8 -4
- package/src/lib/components/slider/components/slider.svelte +2 -1
- package/src/lib/components/tabs/components/tabs-content.svelte +12 -7
- package/src/lib/components/tabs/components/tabs-list.svelte +9 -6
- package/src/lib/components/tabs/components/tabs-trigger.svelte +15 -8
- package/src/lib/components/tabs/components/tabs.svelte +28 -17
- package/src/lib/components/tabs/tabs.svelte.ts +51 -67
- package/src/lib/components/time-field/time-field.svelte.ts +2 -2
- package/src/lib/components/toggle/components/toggle.svelte +19 -14
- package/src/lib/components/toggle/toggle.svelte.ts +15 -20
- package/src/lib/components/toggle-group/components/toggle-group-item.svelte +15 -8
- package/src/lib/components/toggle-group/components/toggle-group.svelte +28 -16
- package/src/lib/components/toggle-group/toggle-group.svelte.ts +33 -35
- package/src/lib/components/toolbar/components/toolbar-group-item.svelte +15 -8
- package/src/lib/components/toolbar/components/toolbar-group.svelte +19 -14
- package/src/lib/components/toolbar/toolbar.svelte.ts +28 -30
- package/src/lib/components/tooltip/components/tooltip-content.svelte +15 -8
- package/src/lib/components/tooltip/components/tooltip-provider.svelte +18 -7
- package/src/lib/components/tooltip/components/tooltip-trigger.svelte +21 -10
- package/src/lib/components/tooltip/components/tooltip.svelte +34 -21
- package/src/lib/components/tooltip/tooltip.svelte.ts +101 -125
- package/src/lib/internal/dismissible-layer/use-dismissable-layer.svelte.ts +3 -4
- package/src/lib/internal/escape-layer/use-escape-layer.svelte.ts +13 -29
- package/src/lib/internal/floating-layer/use-floating-layer.svelte.ts +26 -30
- package/src/lib/internal/group-value.svelte.ts +11 -10
- package/src/lib/internal/native-popover.svelte.ts +6 -6
- package/src/lib/internal/{roving-focus-group.ts → roving-focus-group.svelte.ts} +17 -19
- package/src/lib/internal/roving-focus-item.svelte.ts +12 -17
- package/src/lib/internal/selection.svelte.ts +55 -41
- package/src/lib/internal/{state-machine.ts → state-machine.svelte.ts} +6 -8
- package/src/lib/internal/timeout-fn.svelte.ts +6 -4
- package/src/lib/internal/tools/box.svelte.ts +5 -114
- package/src/lib/internal/tools/index.ts +1 -11
- package/src/lib/internal/tools/types.ts +0 -3
- package/src/lib/internal/typeahead.svelte.ts +9 -13
- package/src/lib/internal/types.ts +6 -0
- package/src/lib/internal/vaul/use-drawer-content.svelte.ts +2 -2
- package/src/lib/internal/vaul/use-drawer-root.svelte.ts +12 -7
- package/src/lib/components/accordion/accordion.svelte.ts +0 -43
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { attachRef
|
|
1
|
+
import { attachRef } from '../../internal/tools/index.js';
|
|
2
2
|
import { createContext, onDestroy, untrack } from 'svelte';
|
|
3
3
|
import { createBitsAttrs, boolToStr, getDataOpenClosed, boolToEmptyStrOrUndef } from '../../internal/attrs.js';
|
|
4
|
-
import type { BitsKeyboardEvent, BitsMouseEvent, OnChangeFn, RefAttachment,
|
|
4
|
+
import type { BitsKeyboardEvent, BitsMouseEvent, OnChangeFn, RefAttachment, RefOpts } from '../../internal/types.js';
|
|
5
5
|
import { kbd } from '../../internal/kbd.js';
|
|
6
6
|
import { useOpenChangeComplete } from '../../internal/animations-settled.svelte.js';
|
|
7
7
|
|
|
@@ -19,12 +19,10 @@ const [getDialogRoot, setDialogRoot, hasDialogRoot] = createContext<DialogRootSt
|
|
|
19
19
|
import { OpenCell as DialogState } from '../../internal/open-cell.svelte.js';
|
|
20
20
|
export { DialogState };
|
|
21
21
|
|
|
22
|
-
interface DialogRootStateOpts
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}> {
|
|
27
|
-
cell: DialogState;
|
|
22
|
+
interface DialogRootStateOpts {
|
|
23
|
+
readonly variant: DialogVariant;
|
|
24
|
+
readonly onOpenChangeComplete: OnChangeFn<boolean>;
|
|
25
|
+
open: boolean;
|
|
28
26
|
}
|
|
29
27
|
|
|
30
28
|
export class DialogRootState {
|
|
@@ -34,7 +32,6 @@ export class DialogRootState {
|
|
|
34
32
|
}
|
|
35
33
|
|
|
36
34
|
readonly opts: DialogRootStateOpts;
|
|
37
|
-
readonly cell: DialogState;
|
|
38
35
|
triggerNode = $state<HTMLElement | null>(null);
|
|
39
36
|
contentNode = $state<HTMLElement | null>(null);
|
|
40
37
|
descriptionNode = $state<HTMLElement | null>(null);
|
|
@@ -42,33 +39,32 @@ export class DialogRootState {
|
|
|
42
39
|
triggerId = $state<string | undefined>(undefined);
|
|
43
40
|
titleState = $state<DialogTitleState | null>(null);
|
|
44
41
|
descriptionState = $state<DialogDescriptionState | null>(null);
|
|
45
|
-
readonly titleId = $derived.by(() => this.titleState?.opts.id
|
|
46
|
-
readonly descriptionId = $derived.by(() => this.descriptionState?.opts.id
|
|
42
|
+
readonly titleId = $derived.by(() => this.titleState?.opts.id);
|
|
43
|
+
readonly descriptionId = $derived.by(() => this.descriptionState?.opts.id);
|
|
47
44
|
cancelNode = $state<HTMLElement | null>(null);
|
|
48
45
|
nestedOpenCount = $state(0);
|
|
49
46
|
readonly depth: number;
|
|
50
47
|
readonly parent: DialogRootState | null;
|
|
51
48
|
readonly #completion: { readonly pending: boolean };
|
|
52
49
|
/** Rendered: open, or closing with the exit still settling — the window the headless content's scroll lock covers. */
|
|
53
|
-
readonly present = $derived.by(() => this.
|
|
50
|
+
readonly present = $derived.by(() => this.opts.open || this.#completion.pending);
|
|
54
51
|
|
|
55
52
|
constructor(opts: DialogRootStateOpts, parent: DialogRootState | null) {
|
|
56
53
|
this.opts = opts;
|
|
57
|
-
this.cell = opts.cell;
|
|
58
54
|
this.parent = parent;
|
|
59
55
|
this.depth = parent ? parent.depth + 1 : 0;
|
|
60
56
|
this.handleOpen = this.handleOpen.bind(this);
|
|
61
57
|
this.handleClose = this.handleClose.bind(this);
|
|
62
58
|
|
|
63
59
|
this.#completion = useOpenChangeComplete(
|
|
64
|
-
() => this.
|
|
60
|
+
() => this.opts.open,
|
|
65
61
|
() => this.contentNode,
|
|
66
|
-
(isOpen) => this.opts.onOpenChangeComplete
|
|
62
|
+
(isOpen) => this.opts.onOpenChangeComplete(isOpen),
|
|
67
63
|
);
|
|
68
64
|
|
|
69
65
|
let started = false;
|
|
70
66
|
$effect(() => {
|
|
71
|
-
const isOpen = this.
|
|
67
|
+
const isOpen = this.opts.open;
|
|
72
68
|
if (!started) {
|
|
73
69
|
started = true;
|
|
74
70
|
return;
|
|
@@ -84,22 +80,22 @@ export class DialogRootState {
|
|
|
84
80
|
});
|
|
85
81
|
|
|
86
82
|
onDestroy(() => {
|
|
87
|
-
if (this.
|
|
83
|
+
if (this.opts.open) {
|
|
88
84
|
this.parent?.decrementNested();
|
|
89
85
|
}
|
|
90
86
|
});
|
|
91
87
|
}
|
|
92
88
|
|
|
93
89
|
handleOpen() {
|
|
94
|
-
this.
|
|
90
|
+
this.opts.open = true;
|
|
95
91
|
}
|
|
96
92
|
|
|
97
93
|
handleClose() {
|
|
98
|
-
this.
|
|
94
|
+
this.opts.open = false;
|
|
99
95
|
}
|
|
100
96
|
|
|
101
97
|
getBitsAttr: typeof dialogAttrs.getAttr = (part) => {
|
|
102
|
-
return dialogAttrs.getAttr(part, this.opts.variant
|
|
98
|
+
return dialogAttrs.getAttr(part, this.opts.variant);
|
|
103
99
|
};
|
|
104
100
|
|
|
105
101
|
incrementNested() {
|
|
@@ -116,12 +112,14 @@ export class DialogRootState {
|
|
|
116
112
|
readonly sharedProps = $derived.by(
|
|
117
113
|
() =>
|
|
118
114
|
({
|
|
119
|
-
'data-state': getDataOpenClosed(this.
|
|
115
|
+
'data-state': getDataOpenClosed(this.opts.open),
|
|
120
116
|
}) as const,
|
|
121
117
|
);
|
|
122
118
|
}
|
|
123
119
|
|
|
124
|
-
interface DialogTriggerStateOpts extends
|
|
120
|
+
interface DialogTriggerStateOpts extends RefOpts {
|
|
121
|
+
readonly disabled: boolean;
|
|
122
|
+
}
|
|
125
123
|
|
|
126
124
|
export class DialogTriggerState {
|
|
127
125
|
static create(opts: DialogTriggerStateOpts) {
|
|
@@ -135,22 +133,25 @@ export class DialogTriggerState {
|
|
|
135
133
|
constructor(opts: DialogTriggerStateOpts, root: DialogRootState) {
|
|
136
134
|
this.opts = opts;
|
|
137
135
|
this.root = root;
|
|
138
|
-
this.attachment = attachRef(
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
136
|
+
this.attachment = attachRef<HTMLElement>(
|
|
137
|
+
(v) => (opts.ref = v),
|
|
138
|
+
(v) => {
|
|
139
|
+
this.root.triggerNode = v;
|
|
140
|
+
this.root.triggerId = v?.id;
|
|
141
|
+
},
|
|
142
|
+
);
|
|
142
143
|
this.onclick = this.onclick.bind(this);
|
|
143
144
|
this.onkeydown = this.onkeydown.bind(this);
|
|
144
145
|
}
|
|
145
146
|
|
|
146
147
|
onclick(e: BitsMouseEvent) {
|
|
147
|
-
if (this.opts.disabled
|
|
148
|
+
if (this.opts.disabled) return;
|
|
148
149
|
if (e.button > 0) return;
|
|
149
150
|
this.root.handleOpen();
|
|
150
151
|
}
|
|
151
152
|
|
|
152
153
|
onkeydown(e: BitsKeyboardEvent) {
|
|
153
|
-
if (this.opts.disabled
|
|
154
|
+
if (this.opts.disabled) return;
|
|
154
155
|
if (e.key === kbd.SPACE || e.key === kbd.ENTER) {
|
|
155
156
|
e.preventDefault();
|
|
156
157
|
this.root.handleOpen();
|
|
@@ -160,21 +161,24 @@ export class DialogTriggerState {
|
|
|
160
161
|
readonly props = $derived.by(
|
|
161
162
|
() =>
|
|
162
163
|
({
|
|
163
|
-
id: this.opts.id
|
|
164
|
+
id: this.opts.id,
|
|
164
165
|
'aria-haspopup': 'dialog',
|
|
165
|
-
'aria-expanded': boolToStr(this.root.
|
|
166
|
+
'aria-expanded': boolToStr(this.root.opts.open),
|
|
166
167
|
'aria-controls': this.root.contentId,
|
|
167
168
|
[this.root.getBitsAttr('trigger')]: '',
|
|
168
169
|
onkeydown: this.onkeydown,
|
|
169
170
|
onclick: this.onclick,
|
|
170
|
-
disabled: this.opts.disabled
|
|
171
|
+
disabled: this.opts.disabled ? true : undefined,
|
|
171
172
|
...this.root.sharedProps,
|
|
172
173
|
...this.attachment,
|
|
173
174
|
}) as const,
|
|
174
175
|
);
|
|
175
176
|
}
|
|
176
177
|
|
|
177
|
-
interface DialogCloseStateOpts extends
|
|
178
|
+
interface DialogCloseStateOpts extends RefOpts {
|
|
179
|
+
readonly variant: 'action' | 'cancel' | 'close';
|
|
180
|
+
readonly disabled: boolean;
|
|
181
|
+
}
|
|
178
182
|
|
|
179
183
|
export class DialogCloseState {
|
|
180
184
|
static create(opts: DialogCloseStateOpts) {
|
|
@@ -188,19 +192,19 @@ export class DialogCloseState {
|
|
|
188
192
|
constructor(opts: DialogCloseStateOpts, root: DialogRootState) {
|
|
189
193
|
this.opts = opts;
|
|
190
194
|
this.root = root;
|
|
191
|
-
this.attachment = attachRef(
|
|
195
|
+
this.attachment = attachRef<HTMLElement>((v) => (opts.ref = v));
|
|
192
196
|
this.onclick = this.onclick.bind(this);
|
|
193
197
|
this.onkeydown = this.onkeydown.bind(this);
|
|
194
198
|
}
|
|
195
199
|
|
|
196
200
|
onclick(e: BitsMouseEvent) {
|
|
197
|
-
if (this.opts.disabled
|
|
201
|
+
if (this.opts.disabled) return;
|
|
198
202
|
if (e.button > 0) return;
|
|
199
203
|
this.root.handleClose();
|
|
200
204
|
}
|
|
201
205
|
|
|
202
206
|
onkeydown(e: BitsKeyboardEvent) {
|
|
203
|
-
if (this.opts.disabled
|
|
207
|
+
if (this.opts.disabled) return;
|
|
204
208
|
if (e.key === kbd.SPACE || e.key === kbd.ENTER) {
|
|
205
209
|
e.preventDefault();
|
|
206
210
|
this.root.handleClose();
|
|
@@ -210,11 +214,11 @@ export class DialogCloseState {
|
|
|
210
214
|
readonly props = $derived.by(
|
|
211
215
|
() =>
|
|
212
216
|
({
|
|
213
|
-
id: this.opts.id
|
|
214
|
-
[this.root.getBitsAttr(this.opts.variant
|
|
217
|
+
id: this.opts.id,
|
|
218
|
+
[this.root.getBitsAttr(this.opts.variant)]: '',
|
|
215
219
|
onclick: this.onclick,
|
|
216
220
|
onkeydown: this.onkeydown,
|
|
217
|
-
disabled: this.opts.disabled
|
|
221
|
+
disabled: this.opts.disabled ? true : undefined,
|
|
218
222
|
tabindex: 0,
|
|
219
223
|
...this.root.sharedProps,
|
|
220
224
|
...this.attachment,
|
|
@@ -222,7 +226,9 @@ export class DialogCloseState {
|
|
|
222
226
|
);
|
|
223
227
|
}
|
|
224
228
|
|
|
225
|
-
interface DialogTitleStateOpts extends
|
|
229
|
+
interface DialogTitleStateOpts extends RefOpts {
|
|
230
|
+
readonly level: 1 | 2 | 3 | 4 | 5 | 6;
|
|
231
|
+
}
|
|
226
232
|
|
|
227
233
|
export class DialogTitleState {
|
|
228
234
|
static create(opts: DialogTitleStateOpts) {
|
|
@@ -237,15 +243,15 @@ export class DialogTitleState {
|
|
|
237
243
|
this.opts = opts;
|
|
238
244
|
this.root = root;
|
|
239
245
|
this.root.titleState = this;
|
|
240
|
-
this.attachment = attachRef(
|
|
246
|
+
this.attachment = attachRef<HTMLElement>((v) => (opts.ref = v));
|
|
241
247
|
}
|
|
242
248
|
|
|
243
249
|
readonly props = $derived.by(
|
|
244
250
|
() =>
|
|
245
251
|
({
|
|
246
|
-
id: this.opts.id
|
|
252
|
+
id: this.opts.id,
|
|
247
253
|
role: 'heading',
|
|
248
|
-
'aria-level': this.opts.level
|
|
254
|
+
'aria-level': this.opts.level,
|
|
249
255
|
[this.root.getBitsAttr('title')]: '',
|
|
250
256
|
...this.root.sharedProps,
|
|
251
257
|
...this.attachment,
|
|
@@ -253,7 +259,7 @@ export class DialogTitleState {
|
|
|
253
259
|
);
|
|
254
260
|
}
|
|
255
261
|
|
|
256
|
-
interface DialogDescriptionStateOpts extends
|
|
262
|
+
interface DialogDescriptionStateOpts extends RefOpts {}
|
|
257
263
|
|
|
258
264
|
export class DialogDescriptionState {
|
|
259
265
|
static create(opts: DialogDescriptionStateOpts) {
|
|
@@ -268,15 +274,18 @@ export class DialogDescriptionState {
|
|
|
268
274
|
this.opts = opts;
|
|
269
275
|
this.root = root;
|
|
270
276
|
this.root.descriptionState = this;
|
|
271
|
-
this.attachment = attachRef(
|
|
272
|
-
|
|
273
|
-
|
|
277
|
+
this.attachment = attachRef<HTMLElement>(
|
|
278
|
+
(v) => (opts.ref = v),
|
|
279
|
+
(v) => {
|
|
280
|
+
this.root.descriptionNode = v;
|
|
281
|
+
},
|
|
282
|
+
);
|
|
274
283
|
}
|
|
275
284
|
|
|
276
285
|
readonly props = $derived.by(
|
|
277
286
|
() =>
|
|
278
287
|
({
|
|
279
|
-
id: this.opts.id
|
|
288
|
+
id: this.opts.id,
|
|
280
289
|
[this.root.getBitsAttr('description')]: '',
|
|
281
290
|
...this.root.sharedProps,
|
|
282
291
|
...this.attachment,
|
|
@@ -284,7 +293,7 @@ export class DialogDescriptionState {
|
|
|
284
293
|
);
|
|
285
294
|
}
|
|
286
295
|
|
|
287
|
-
interface DialogContentStateOpts extends
|
|
296
|
+
interface DialogContentStateOpts extends RefOpts {}
|
|
288
297
|
|
|
289
298
|
export class DialogContentState {
|
|
290
299
|
static create(opts: DialogContentStateOpts) {
|
|
@@ -298,26 +307,29 @@ export class DialogContentState {
|
|
|
298
307
|
constructor(opts: DialogContentStateOpts, root: DialogRootState) {
|
|
299
308
|
this.opts = opts;
|
|
300
309
|
this.root = root;
|
|
301
|
-
this.attachment = attachRef(
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
310
|
+
this.attachment = attachRef<HTMLElement>(
|
|
311
|
+
(v) => (opts.ref = v),
|
|
312
|
+
(v) => {
|
|
313
|
+
this.root.contentNode = v;
|
|
314
|
+
this.root.contentId = v?.id;
|
|
315
|
+
},
|
|
316
|
+
);
|
|
305
317
|
}
|
|
306
318
|
|
|
307
|
-
readonly snippetProps = $derived.by(() => ({ open: this.root.
|
|
319
|
+
readonly snippetProps = $derived.by(() => ({ open: this.root.opts.open }));
|
|
308
320
|
|
|
309
321
|
readonly props = $derived.by(
|
|
310
322
|
() =>
|
|
311
323
|
({
|
|
312
|
-
id: this.opts.id
|
|
313
|
-
role: this.root.opts.variant
|
|
324
|
+
id: this.opts.id,
|
|
325
|
+
role: this.root.opts.variant === 'alert-dialog' ? 'alertdialog' : 'dialog',
|
|
314
326
|
'aria-modal': 'true',
|
|
315
327
|
'aria-describedby': this.root.descriptionId,
|
|
316
328
|
'aria-labelledby': this.root.titleId,
|
|
317
329
|
[this.root.getBitsAttr('content')]: '',
|
|
318
330
|
style: {
|
|
319
331
|
pointerEvents: 'auto',
|
|
320
|
-
outline: this.root.opts.variant
|
|
332
|
+
outline: this.root.opts.variant === 'alert-dialog' ? 'none' : undefined,
|
|
321
333
|
'--bits-dialog-depth': this.root.depth,
|
|
322
334
|
'--bits-dialog-nested-count': this.root.nestedOpenCount,
|
|
323
335
|
// CSS containment isolates style/layout calculations from the rest of the page,
|
|
@@ -325,7 +337,7 @@ export class DialogContentState {
|
|
|
325
337
|
// Paint is omitted so tooltips/selects can render outside dialog bounds.
|
|
326
338
|
contain: 'layout style',
|
|
327
339
|
},
|
|
328
|
-
tabindex: this.root.opts.variant
|
|
340
|
+
tabindex: this.root.opts.variant === 'alert-dialog' ? -1 : undefined,
|
|
329
341
|
'data-nested-open': boolToEmptyStrOrUndef(this.root.nestedOpenCount > 0),
|
|
330
342
|
'data-nested': boolToEmptyStrOrUndef(this.root.parent !== null),
|
|
331
343
|
...this.root.sharedProps,
|
|
@@ -334,7 +346,7 @@ export class DialogContentState {
|
|
|
334
346
|
);
|
|
335
347
|
}
|
|
336
348
|
|
|
337
|
-
interface DialogOverlayStateOpts extends
|
|
349
|
+
interface DialogOverlayStateOpts extends RefOpts {}
|
|
338
350
|
|
|
339
351
|
export class DialogOverlayState {
|
|
340
352
|
static create(opts: DialogOverlayStateOpts) {
|
|
@@ -347,15 +359,15 @@ export class DialogOverlayState {
|
|
|
347
359
|
constructor(opts: DialogOverlayStateOpts, root: DialogRootState) {
|
|
348
360
|
this.opts = opts;
|
|
349
361
|
this.root = root;
|
|
350
|
-
this.attachment = attachRef(
|
|
362
|
+
this.attachment = attachRef<HTMLElement>((v) => (opts.ref = v));
|
|
351
363
|
}
|
|
352
364
|
|
|
353
|
-
readonly snippetProps = $derived.by(() => ({ open: this.root.
|
|
365
|
+
readonly snippetProps = $derived.by(() => ({ open: this.root.opts.open }));
|
|
354
366
|
|
|
355
367
|
readonly props = $derived.by(
|
|
356
368
|
() =>
|
|
357
369
|
({
|
|
358
|
-
id: this.opts.id
|
|
370
|
+
id: this.opts.id,
|
|
359
371
|
[this.root.getBitsAttr('overlay')]: '',
|
|
360
372
|
style: {
|
|
361
373
|
pointerEvents: 'auto',
|
|
@@ -118,9 +118,14 @@
|
|
|
118
118
|
});
|
|
119
119
|
|
|
120
120
|
DialogRootState.create({
|
|
121
|
-
variant:
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
variant: 'dialog',
|
|
122
|
+
get open() {
|
|
123
|
+
return dialogCell.open;
|
|
124
|
+
},
|
|
125
|
+
set open(v) {
|
|
126
|
+
dialogCell.open = v;
|
|
127
|
+
},
|
|
128
|
+
onOpenChangeComplete: handleOpenChangeComplete,
|
|
124
129
|
});
|
|
125
130
|
</script>
|
|
126
131
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { boxWith } from '../../../internal/tools/index.js';
|
|
3
2
|
import { mergeProps } from '../../../internal/merge-props.js';
|
|
4
3
|
import type { LinkPreviewContentProps } from '../types.js';
|
|
5
4
|
import { LinkPreviewContentState } from '../link-preview.svelte.js';
|
|
@@ -25,13 +24,21 @@
|
|
|
25
24
|
}: LinkPreviewContentProps = $props();
|
|
26
25
|
|
|
27
26
|
const contentState = LinkPreviewContentState.create({
|
|
28
|
-
id
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
onEscapeKeydown
|
|
27
|
+
get id() {
|
|
28
|
+
return id;
|
|
29
|
+
},
|
|
30
|
+
get onInteractOutside() {
|
|
31
|
+
return onInteractOutside;
|
|
32
|
+
},
|
|
33
|
+
get onEscapeKeydown() {
|
|
34
|
+
return onEscapeKeydown;
|
|
35
|
+
},
|
|
36
|
+
get ref() {
|
|
37
|
+
return ref;
|
|
38
|
+
},
|
|
39
|
+
set ref(v) {
|
|
40
|
+
ref = v;
|
|
41
|
+
},
|
|
35
42
|
});
|
|
36
43
|
|
|
37
44
|
const lifecycleProps = useNativePopoverLifecycle(contentState);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { boxWith } from '../../../internal/tools/index.js';
|
|
3
2
|
import { mergeProps } from '../../../internal/merge-props.js';
|
|
4
3
|
import type { LinkPreviewTriggerProps } from '../types.js';
|
|
5
4
|
import { LinkPreviewTriggerState } from '../link-preview.svelte.js';
|
|
@@ -10,11 +9,15 @@
|
|
|
10
9
|
let { ref = $bindable(null), id = createId(uid), child, children, ...restProps }: LinkPreviewTriggerProps = $props();
|
|
11
10
|
|
|
12
11
|
const triggerState = LinkPreviewTriggerState.create({
|
|
13
|
-
id
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
get id() {
|
|
13
|
+
return id;
|
|
14
|
+
},
|
|
15
|
+
get ref() {
|
|
16
|
+
return ref;
|
|
17
|
+
},
|
|
18
|
+
set ref(v) {
|
|
19
|
+
ref = v;
|
|
20
|
+
},
|
|
18
21
|
});
|
|
19
22
|
|
|
20
23
|
const mergedProps = $derived(mergeProps({ 'data-slot': 'hover-card-trigger' }, restProps, triggerState.props));
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { boxWith } from '../../../internal/tools/index.js';
|
|
3
2
|
import { OpenCell } from '../../../internal/open-cell.svelte.js';
|
|
4
3
|
import type { LinkPreviewRootProps } from '../types.js';
|
|
5
4
|
import { LinkPreviewRootState } from '../link-preview.svelte.js';
|
|
@@ -14,20 +13,29 @@
|
|
|
14
13
|
children,
|
|
15
14
|
}: LinkPreviewRootProps = $props();
|
|
16
15
|
|
|
17
|
-
// Cell over the source prop; the engine
|
|
18
|
-
// ADR 0006) — the box is a bridge over the cell, so the cell owns `open`.
|
|
16
|
+
// Cell over the source prop; the cell owns `open`, the engine reads and writes it through it.
|
|
19
17
|
// svelte-ignore state_referenced_locally
|
|
20
18
|
const cell = givenCell ?? new OpenCell(() => open);
|
|
21
19
|
|
|
22
20
|
LinkPreviewRootState.create({
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
21
|
+
get open() {
|
|
22
|
+
return cell.open;
|
|
23
|
+
},
|
|
24
|
+
set open(v) {
|
|
25
|
+
cell.open = v;
|
|
26
|
+
},
|
|
27
|
+
get disabled() {
|
|
28
|
+
return disabled;
|
|
29
|
+
},
|
|
30
|
+
get openDelay() {
|
|
31
|
+
return openDelay;
|
|
32
|
+
},
|
|
33
|
+
get closeDelay() {
|
|
34
|
+
return closeDelay;
|
|
35
|
+
},
|
|
36
|
+
get onOpenChangeComplete() {
|
|
37
|
+
return onOpenChangeComplete;
|
|
38
|
+
},
|
|
31
39
|
});
|
|
32
40
|
</script>
|
|
33
41
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { attachRef, DOMContext
|
|
1
|
+
import { attachRef, DOMContext } from '../../internal/tools/index.js';
|
|
2
2
|
import { on } from 'svelte/events';
|
|
3
3
|
import { createContext, onDestroy, untrack } from 'svelte';
|
|
4
4
|
import { createBitsAttrs, boolToStr, getDataOpenClosed } from '../../internal/attrs.js';
|
|
5
5
|
import { isElement } from '../../internal/tools/utils/dom.js';
|
|
6
|
-
import type { BitsFocusEvent, BitsPointerEvent, OnChangeFn, RefAttachment,
|
|
6
|
+
import type { BitsFocusEvent, BitsPointerEvent, OnChangeFn, RefAttachment, RefOpts } from '../../internal/types.js';
|
|
7
7
|
import { getTabbableCandidates } from '../../internal/tabbable.js';
|
|
8
8
|
import { SafePolygon } from '../../internal/safe-polygon.svelte.js';
|
|
9
9
|
import { createEffectTimeout } from '../../internal/timeout-fn.svelte.js';
|
|
@@ -15,17 +15,13 @@ const linkPreviewAttrs = createBitsAttrs({
|
|
|
15
15
|
|
|
16
16
|
const [getLinkPreviewRoot, setLinkPreviewRoot] = createContext<LinkPreviewRootState>();
|
|
17
17
|
|
|
18
|
-
interface LinkPreviewRootStateOpts
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
openDelay: number;
|
|
26
|
-
closeDelay: number;
|
|
27
|
-
onOpenChangeComplete: OnChangeFn<boolean>;
|
|
28
|
-
}> {}
|
|
18
|
+
interface LinkPreviewRootStateOpts {
|
|
19
|
+
open: boolean;
|
|
20
|
+
readonly disabled: boolean;
|
|
21
|
+
readonly openDelay: number;
|
|
22
|
+
readonly closeDelay: number;
|
|
23
|
+
readonly onOpenChangeComplete: OnChangeFn<boolean>;
|
|
24
|
+
}
|
|
29
25
|
|
|
30
26
|
export class LinkPreviewRootState {
|
|
31
27
|
static create(opts: LinkPreviewRootStateOpts) {
|
|
@@ -41,17 +37,17 @@ export class LinkPreviewRootState {
|
|
|
41
37
|
triggerNode = $state<HTMLElement | null>(null);
|
|
42
38
|
domContext: DOMContext = new DOMContext(() => null);
|
|
43
39
|
#openTimer = createEffectTimeout(() => {
|
|
44
|
-
this.opts.open
|
|
45
|
-
}, () => this.opts.openDelay
|
|
40
|
+
this.opts.open = true;
|
|
41
|
+
}, () => this.opts.openDelay);
|
|
46
42
|
#closeTimer = createEffectTimeout(() => {
|
|
47
|
-
this.opts.open
|
|
48
|
-
}, () => this.opts.closeDelay
|
|
43
|
+
this.opts.open = false;
|
|
44
|
+
}, () => this.opts.closeDelay);
|
|
49
45
|
|
|
50
46
|
constructor(opts: LinkPreviewRootStateOpts) {
|
|
51
47
|
this.opts = opts;
|
|
52
48
|
|
|
53
49
|
$effect(() => {
|
|
54
|
-
const isOpen = this.opts.open
|
|
50
|
+
const isOpen = this.opts.open;
|
|
55
51
|
return untrack(() => {
|
|
56
52
|
if (!isOpen) {
|
|
57
53
|
this.hasSelection = false;
|
|
@@ -99,13 +95,13 @@ export class LinkPreviewRootState {
|
|
|
99
95
|
|
|
100
96
|
handleOpen() {
|
|
101
97
|
this.clearTimeout();
|
|
102
|
-
if (this.opts.open
|
|
98
|
+
if (this.opts.open || this.opts.disabled) return;
|
|
103
99
|
this.#openTimer.start();
|
|
104
100
|
}
|
|
105
101
|
|
|
106
102
|
immediateClose() {
|
|
107
103
|
this.clearTimeout();
|
|
108
|
-
this.opts.open
|
|
104
|
+
this.opts.open = false;
|
|
109
105
|
}
|
|
110
106
|
|
|
111
107
|
handleClose() {
|
|
@@ -117,7 +113,7 @@ export class LinkPreviewRootState {
|
|
|
117
113
|
}
|
|
118
114
|
}
|
|
119
115
|
|
|
120
|
-
interface LinkPreviewTriggerStateOpts extends
|
|
116
|
+
interface LinkPreviewTriggerStateOpts extends RefOpts {}
|
|
121
117
|
|
|
122
118
|
export class LinkPreviewTriggerState {
|
|
123
119
|
static create(opts: LinkPreviewTriggerStateOpts) {
|
|
@@ -131,8 +127,11 @@ export class LinkPreviewTriggerState {
|
|
|
131
127
|
constructor(opts: LinkPreviewTriggerStateOpts, root: LinkPreviewRootState) {
|
|
132
128
|
this.opts = opts;
|
|
133
129
|
this.root = root;
|
|
134
|
-
this.attachment = attachRef(
|
|
135
|
-
|
|
130
|
+
this.attachment = attachRef<HTMLElement>(
|
|
131
|
+
(v) => (opts.ref = v),
|
|
132
|
+
(v) => (this.root.triggerNode = v),
|
|
133
|
+
);
|
|
134
|
+
this.root.domContext = new DOMContext(() => opts.ref);
|
|
136
135
|
this.onpointerenter = this.onpointerenter.bind(this);
|
|
137
136
|
this.onpointerleave = this.onpointerleave.bind(this);
|
|
138
137
|
this.onfocus = this.onfocus.bind(this);
|
|
@@ -147,7 +146,7 @@ export class LinkPreviewTriggerState {
|
|
|
147
146
|
onpointerleave(e: BitsPointerEvent) {
|
|
148
147
|
if (e.pointerType === 'touch') return;
|
|
149
148
|
// The content is always mounted (native popover, display toggled), so open is the only gate.
|
|
150
|
-
if (!this.root.opts.open
|
|
149
|
+
if (!this.root.opts.open) {
|
|
151
150
|
this.root.immediateClose();
|
|
152
151
|
}
|
|
153
152
|
}
|
|
@@ -164,10 +163,10 @@ export class LinkPreviewTriggerState {
|
|
|
164
163
|
readonly props = $derived.by(
|
|
165
164
|
() =>
|
|
166
165
|
({
|
|
167
|
-
id: this.opts.id
|
|
166
|
+
id: this.opts.id,
|
|
168
167
|
'aria-haspopup': 'dialog',
|
|
169
|
-
'aria-expanded': boolToStr(this.root.opts.open
|
|
170
|
-
'data-state': getDataOpenClosed(this.root.opts.open
|
|
168
|
+
'aria-expanded': boolToStr(this.root.opts.open),
|
|
169
|
+
'data-state': getDataOpenClosed(this.root.opts.open),
|
|
171
170
|
'aria-controls': this.root.contentNode?.id,
|
|
172
171
|
role: 'button',
|
|
173
172
|
[linkPreviewAttrs.trigger]: '',
|
|
@@ -180,13 +179,10 @@ export class LinkPreviewTriggerState {
|
|
|
180
179
|
);
|
|
181
180
|
}
|
|
182
181
|
|
|
183
|
-
interface LinkPreviewContentStateOpts
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
onInteractOutside: (e: PointerEvent) => void;
|
|
188
|
-
onEscapeKeydown: (e: KeyboardEvent) => void;
|
|
189
|
-
}> {}
|
|
182
|
+
interface LinkPreviewContentStateOpts extends RefOpts {
|
|
183
|
+
readonly onInteractOutside: (e: PointerEvent) => void;
|
|
184
|
+
readonly onEscapeKeydown: (e: KeyboardEvent) => void;
|
|
185
|
+
}
|
|
190
186
|
|
|
191
187
|
export class LinkPreviewContentState {
|
|
192
188
|
static create(opts: LinkPreviewContentStateOpts) {
|
|
@@ -200,16 +196,19 @@ export class LinkPreviewContentState {
|
|
|
200
196
|
constructor(opts: LinkPreviewContentStateOpts, root: LinkPreviewRootState) {
|
|
201
197
|
this.opts = opts;
|
|
202
198
|
this.root = root;
|
|
203
|
-
this.attachment = attachRef(
|
|
204
|
-
|
|
199
|
+
this.attachment = attachRef<HTMLElement>(
|
|
200
|
+
(v) => (opts.ref = v),
|
|
201
|
+
(v) => (this.root.contentNode = v),
|
|
202
|
+
);
|
|
203
|
+
this.root.domContext = new DOMContext(() => opts.ref);
|
|
205
204
|
this.onpointerdown = this.onpointerdown.bind(this);
|
|
206
205
|
this.onpointerenter = this.onpointerenter.bind(this);
|
|
207
206
|
this.onfocusout = this.onfocusout.bind(this);
|
|
208
207
|
|
|
209
208
|
new SafePolygon({
|
|
210
209
|
triggerNode: () => this.root.triggerNode,
|
|
211
|
-
contentNode: () => this.opts.ref
|
|
212
|
-
enabled: () => this.root.opts.open
|
|
210
|
+
contentNode: () => this.opts.ref,
|
|
211
|
+
enabled: () => this.root.opts.open,
|
|
213
212
|
onPointerExit: () => {
|
|
214
213
|
this.root.handleClose();
|
|
215
214
|
},
|
|
@@ -241,25 +240,25 @@ export class LinkPreviewContentState {
|
|
|
241
240
|
}
|
|
242
241
|
|
|
243
242
|
onInteractOutside = (e: PointerEvent) => {
|
|
244
|
-
this.opts.onInteractOutside
|
|
243
|
+
this.opts.onInteractOutside(e);
|
|
245
244
|
if (e.defaultPrevented) return;
|
|
246
245
|
this.root.handleClose();
|
|
247
246
|
};
|
|
248
247
|
|
|
249
248
|
onEscapeKeydown = (e: KeyboardEvent) => {
|
|
250
|
-
this.opts.onEscapeKeydown
|
|
249
|
+
this.opts.onEscapeKeydown?.(e);
|
|
251
250
|
if (e.defaultPrevented) return;
|
|
252
251
|
this.root.handleClose();
|
|
253
252
|
};
|
|
254
253
|
|
|
255
|
-
readonly snippetProps = $derived.by(() => ({ open: this.root.opts.open
|
|
254
|
+
readonly snippetProps = $derived.by(() => ({ open: this.root.opts.open }));
|
|
256
255
|
|
|
257
256
|
readonly props = $derived.by(
|
|
258
257
|
() =>
|
|
259
258
|
({
|
|
260
|
-
id: this.opts.id
|
|
259
|
+
id: this.opts.id,
|
|
261
260
|
tabindex: -1,
|
|
262
|
-
'data-state': getDataOpenClosed(this.root.opts.open
|
|
261
|
+
'data-state': getDataOpenClosed(this.root.opts.open),
|
|
263
262
|
[linkPreviewAttrs.content]: '',
|
|
264
263
|
onpointerdown: this.onpointerdown,
|
|
265
264
|
onpointerenter: this.onpointerenter,
|