@dryui/ui 1.3.0 → 1.4.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.
- package/dist/accordion/accordion-content.svelte +1 -1
- package/dist/alert/alert.svelte +1 -1
- package/dist/app-frame/app-frame.svelte +125 -0
- package/dist/app-frame/app-frame.svelte.d.ts +10 -0
- package/dist/app-frame/index.d.ts +8 -0
- package/dist/app-frame/index.js +1 -0
- package/dist/aurora/aurora.svelte +22 -59
- package/dist/beam/beam.svelte +28 -9
- package/dist/carousel/carousel-button-dots.svelte +25 -8
- package/dist/carousel/carousel-button-thumbnails.svelte +25 -8
- package/dist/carousel/carousel-root.svelte +115 -4
- package/dist/carousel/carousel-slide.svelte +5 -1
- package/dist/carousel/carousel-viewport.svelte +2 -0
- package/dist/carousel/context.svelte.d.ts +5 -0
- package/dist/chart/chart-bars.svelte +25 -16
- package/dist/chart/chart-donut.svelte +25 -16
- package/dist/chart/chart-root.svelte +134 -30
- package/dist/chart/chart-root.svelte.d.ts +1 -0
- package/dist/chart/context.svelte.d.ts +3 -1
- package/dist/chart/context.svelte.js +1 -0
- package/dist/chart/index.d.ts +1 -0
- package/dist/chromatic-shift/chromatic-shift.svelte +36 -9
- package/dist/collapsible/collapsible-content.svelte +2 -1
- package/dist/combobox/combobox-content.svelte +26 -44
- package/dist/combobox/combobox-content.svelte.d.ts +1 -1
- package/dist/combobox/combobox-input-root.svelte +7 -1
- package/dist/combobox/combobox-input.svelte +21 -8
- package/dist/country-select/country-select-button-input.svelte +124 -260
- package/dist/date-picker/datepicker-content.svelte +18 -26
- package/dist/date-picker/datepicker-content.svelte.d.ts +2 -1
- package/dist/date-picker/datepicker-input-root.svelte +7 -1
- package/dist/date-range-picker/date-range-picker-content.svelte +18 -14
- package/dist/date-range-picker/date-range-picker-content.svelte.d.ts +2 -1
- package/dist/date-range-picker/date-range-picker-root.svelte +7 -1
- package/dist/displacement/displacement.svelte +16 -22
- package/dist/drag-and-drop/context.svelte.d.ts +2 -0
- package/dist/drag-and-drop/drag-and-drop-handle.svelte +34 -5
- package/dist/drag-and-drop/drag-and-drop-item.svelte +23 -14
- package/dist/drag-and-drop/drag-and-drop-root.svelte +60 -16
- package/dist/god-rays/god-rays.svelte +11 -0
- package/dist/gradient-mesh/gradient-mesh.svelte +27 -5
- package/dist/hover-card/context.svelte.d.ts +1 -10
- package/dist/hover-card/context.svelte.js +1 -2
- package/dist/hover-card/hover-card-content.svelte +41 -3
- package/dist/hover-card/hover-card-root.svelte +7 -55
- package/dist/hover-card/hover-card-trigger.svelte +79 -40
- package/dist/hover-card/hover-card-trigger.svelte.d.ts +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/internal/motion.d.ts +1 -1
- package/dist/internal/motion.js +1 -1
- package/dist/marquee/marquee.svelte +56 -8
- package/dist/mega-menu/context.svelte.d.ts +2 -1
- package/dist/mega-menu/mega-menu-button-trigger.svelte +2 -14
- package/dist/mega-menu/mega-menu-item.svelte +3 -1
- package/dist/mega-menu/mega-menu-panel.svelte +35 -3
- package/dist/mega-menu/mega-menu-root.svelte +28 -13
- package/dist/menubar/context.svelte.d.ts +2 -2
- package/dist/menubar/menubar-button-trigger.svelte +5 -3
- package/dist/menubar/menubar-content.svelte +20 -12
- package/dist/menubar/menubar-root.svelte +4 -4
- package/dist/multi-select-combobox/multi-select-combobox-content.svelte +18 -55
- package/dist/multi-select-combobox/multi-select-combobox-content.svelte.d.ts +1 -1
- package/dist/noise/noise.svelte +38 -6
- package/dist/notification-center/context.svelte.d.ts +0 -1
- package/dist/notification-center/notification-center-panel.svelte +54 -35
- package/dist/notification-center/notification-center-root.svelte +0 -1
- package/dist/notification-center/notification-center-trigger-button.svelte +1 -8
- package/dist/option-picker/option-picker-description.svelte +2 -2
- package/dist/option-picker/option-picker-item.svelte +10 -3
- package/dist/option-picker/option-picker-label.svelte +2 -2
- package/dist/option-picker/option-picker-preview.svelte +18 -13
- package/dist/phone-input/phone-input-select.svelte +2 -152
- package/dist/phone-input/phone-input-select.svelte.d.ts +1 -7
- package/dist/rich-text-editor/rich-text-editor-toolbar-button-input.svelte +84 -29
- package/dist/scroll-area/scroll-area.svelte +16 -4
- package/dist/select/select-content.svelte +21 -31
- package/dist/select/select-content.svelte.d.ts +1 -1
- package/dist/select/select-root-input.svelte +7 -1
- package/dist/shimmer/shimmer.svelte +22 -12
- package/dist/tabs/tabs-list.svelte +12 -0
- package/dist/transfer/transfer-item.svelte +0 -3
- package/dist/transfer/transfer-list-input.svelte +1 -6
- package/dist/tree/context.svelte.d.ts +7 -1
- package/dist/tree/tree-item-children.svelte +12 -10
- package/dist/tree/tree-item-label.svelte +6 -17
- package/dist/tree/tree-item-label.svelte.d.ts +2 -2
- package/dist/tree/tree-item.svelte +28 -1
- package/dist/tree/tree-root.svelte +135 -59
- package/dist/typography/heading.svelte +1 -0
- package/package.json +8 -2
- package/skills/dryui/SKILL.md +1 -0
- package/dist/hover-card/hover-card-root.svelte.d.ts +0 -9
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
-
|
|
4
|
-
interface CountryInfo {
|
|
5
|
-
code: string;
|
|
6
|
-
name: string;
|
|
7
|
-
dialCode: string;
|
|
8
|
-
flag: string;
|
|
9
|
-
format?: string;
|
|
10
|
-
}
|
|
3
|
+
import { COUNTRY_DATA, type CountryInfo } from '@dryui/primitives';
|
|
11
4
|
|
|
12
5
|
interface Props extends Omit<HTMLAttributes<HTMLDivElement>, 'onchange'> {
|
|
13
6
|
value?: string;
|
|
@@ -33,150 +26,7 @@
|
|
|
33
26
|
...rest
|
|
34
27
|
}: Props = $props();
|
|
35
28
|
|
|
36
|
-
const
|
|
37
|
-
{
|
|
38
|
-
code: 'US',
|
|
39
|
-
name: 'United States',
|
|
40
|
-
dialCode: '+1',
|
|
41
|
-
flag: '\u{1F1FA}\u{1F1F8}',
|
|
42
|
-
format: '### ### ####'
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
code: 'GB',
|
|
46
|
-
name: 'United Kingdom',
|
|
47
|
-
dialCode: '+44',
|
|
48
|
-
flag: '\u{1F1EC}\u{1F1E7}',
|
|
49
|
-
format: '#### ######'
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
code: 'CA',
|
|
53
|
-
name: 'Canada',
|
|
54
|
-
dialCode: '+1',
|
|
55
|
-
flag: '\u{1F1E8}\u{1F1E6}',
|
|
56
|
-
format: '### ### ####'
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
code: 'AU',
|
|
60
|
-
name: 'Australia',
|
|
61
|
-
dialCode: '+61',
|
|
62
|
-
flag: '\u{1F1E6}\u{1F1FA}',
|
|
63
|
-
format: '### ### ###'
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
code: 'DE',
|
|
67
|
-
name: 'Germany',
|
|
68
|
-
dialCode: '+49',
|
|
69
|
-
flag: '\u{1F1E9}\u{1F1EA}',
|
|
70
|
-
format: '### #######'
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
code: 'FR',
|
|
74
|
-
name: 'France',
|
|
75
|
-
dialCode: '+33',
|
|
76
|
-
flag: '\u{1F1EB}\u{1F1F7}',
|
|
77
|
-
format: '# ## ## ## ##'
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
code: 'IT',
|
|
81
|
-
name: 'Italy',
|
|
82
|
-
dialCode: '+39',
|
|
83
|
-
flag: '\u{1F1EE}\u{1F1F9}',
|
|
84
|
-
format: '### ### ####'
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
code: 'ES',
|
|
88
|
-
name: 'Spain',
|
|
89
|
-
dialCode: '+34',
|
|
90
|
-
flag: '\u{1F1EA}\u{1F1F8}',
|
|
91
|
-
format: '### ## ## ##'
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
code: 'BR',
|
|
95
|
-
name: 'Brazil',
|
|
96
|
-
dialCode: '+55',
|
|
97
|
-
flag: '\u{1F1E7}\u{1F1F7}',
|
|
98
|
-
format: '## ##### ####'
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
code: 'IN',
|
|
102
|
-
name: 'India',
|
|
103
|
-
dialCode: '+91',
|
|
104
|
-
flag: '\u{1F1EE}\u{1F1F3}',
|
|
105
|
-
format: '##### #####'
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
code: 'CN',
|
|
109
|
-
name: 'China',
|
|
110
|
-
dialCode: '+86',
|
|
111
|
-
flag: '\u{1F1E8}\u{1F1F3}',
|
|
112
|
-
format: '### #### ####'
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
code: 'JP',
|
|
116
|
-
name: 'Japan',
|
|
117
|
-
dialCode: '+81',
|
|
118
|
-
flag: '\u{1F1EF}\u{1F1F5}',
|
|
119
|
-
format: '## #### ####'
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
code: 'KR',
|
|
123
|
-
name: 'South Korea',
|
|
124
|
-
dialCode: '+82',
|
|
125
|
-
flag: '\u{1F1F0}\u{1F1F7}',
|
|
126
|
-
format: '## #### ####'
|
|
127
|
-
},
|
|
128
|
-
{
|
|
129
|
-
code: 'MX',
|
|
130
|
-
name: 'Mexico',
|
|
131
|
-
dialCode: '+52',
|
|
132
|
-
flag: '\u{1F1F2}\u{1F1FD}',
|
|
133
|
-
format: '## #### ####'
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
code: 'RU',
|
|
137
|
-
name: 'Russia',
|
|
138
|
-
dialCode: '+7',
|
|
139
|
-
flag: '\u{1F1F7}\u{1F1FA}',
|
|
140
|
-
format: '### ### ## ##'
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
code: 'SA',
|
|
144
|
-
name: 'Saudi Arabia',
|
|
145
|
-
dialCode: '+966',
|
|
146
|
-
flag: '\u{1F1F8}\u{1F1E6}',
|
|
147
|
-
format: '## ### ####'
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
code: 'AE',
|
|
151
|
-
name: 'United Arab Emirates',
|
|
152
|
-
dialCode: '+971',
|
|
153
|
-
flag: '\u{1F1E6}\u{1F1EA}',
|
|
154
|
-
format: '## ### ####'
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
code: 'SG',
|
|
158
|
-
name: 'Singapore',
|
|
159
|
-
dialCode: '+65',
|
|
160
|
-
flag: '\u{1F1F8}\u{1F1EC}',
|
|
161
|
-
format: '#### ####'
|
|
162
|
-
},
|
|
163
|
-
{
|
|
164
|
-
code: 'NL',
|
|
165
|
-
name: 'Netherlands',
|
|
166
|
-
dialCode: '+31',
|
|
167
|
-
flag: '\u{1F1F3}\u{1F1F1}',
|
|
168
|
-
format: '# ########'
|
|
169
|
-
},
|
|
170
|
-
{
|
|
171
|
-
code: 'SE',
|
|
172
|
-
name: 'Sweden',
|
|
173
|
-
dialCode: '+46',
|
|
174
|
-
flag: '\u{1F1F8}\u{1F1EA}',
|
|
175
|
-
format: '## ### ## ##'
|
|
176
|
-
}
|
|
177
|
-
];
|
|
178
|
-
|
|
179
|
-
const countries = $derived(countriesProp ?? defaultCountries);
|
|
29
|
+
const countries = $derived(countriesProp ?? COUNTRY_DATA);
|
|
180
30
|
let manualCountryCode = $derived(defaultCountry);
|
|
181
31
|
|
|
182
32
|
const orderedCountries = $derived(
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
|
-
|
|
3
|
-
code: string;
|
|
4
|
-
name: string;
|
|
5
|
-
dialCode: string;
|
|
6
|
-
flag: string;
|
|
7
|
-
format?: string;
|
|
8
|
-
}
|
|
2
|
+
import { type CountryInfo } from '@dryui/primitives';
|
|
9
3
|
interface Props extends Omit<HTMLAttributes<HTMLDivElement>, 'onchange'> {
|
|
10
4
|
value?: string;
|
|
11
5
|
defaultCountry?: string;
|
|
@@ -10,11 +10,14 @@
|
|
|
10
10
|
const ctx = getRichTextEditorCtx();
|
|
11
11
|
|
|
12
12
|
let toolbarEl: HTMLDivElement;
|
|
13
|
+
type LinkRequestOrigin = 'toolbar' | 'content';
|
|
13
14
|
|
|
14
15
|
let showLinkInput = $state(false);
|
|
15
16
|
let linkUrl = $state('');
|
|
16
17
|
let linkInputEl = $state<HTMLInputElement>();
|
|
17
18
|
let savedSelection = $state<Range | null>(null);
|
|
19
|
+
let linkReturnFocusEl = $state<HTMLElement | null>(null);
|
|
20
|
+
let linkRequestOrigin = $state<LinkRequestOrigin>('toolbar');
|
|
18
21
|
|
|
19
22
|
const FOCUSABLE_SELECTOR = 'button:not([disabled]), [role="button"]:not([disabled])';
|
|
20
23
|
|
|
@@ -33,6 +36,28 @@
|
|
|
33
36
|
}
|
|
34
37
|
});
|
|
35
38
|
|
|
39
|
+
$effect(() => {
|
|
40
|
+
if (!toolbarEl) return;
|
|
41
|
+
|
|
42
|
+
function handleLinkRequest(event: Event) {
|
|
43
|
+
if (ctx.readonly || !(event.target instanceof Node)) return;
|
|
44
|
+
|
|
45
|
+
const editorRoot = toolbarEl.closest<HTMLElement>('[data-rte-root]');
|
|
46
|
+
if (!editorRoot?.contains(event.target)) return;
|
|
47
|
+
|
|
48
|
+
ctx.updateState();
|
|
49
|
+
openLinkInput(ctx.currentLink, {
|
|
50
|
+
origin: 'content',
|
|
51
|
+
returnFocusTo: ctx.contentEl
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
document.addEventListener('rte-link-request', handleLinkRequest);
|
|
56
|
+
return () => {
|
|
57
|
+
document.removeEventListener('rte-link-request', handleLinkRequest);
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
|
|
36
61
|
function handleKeydown(event: KeyboardEvent) {
|
|
37
62
|
if (
|
|
38
63
|
event.key !== 'ArrowLeft' &&
|
|
@@ -66,11 +91,29 @@
|
|
|
66
91
|
items[nextIndex]!.focus();
|
|
67
92
|
}
|
|
68
93
|
|
|
69
|
-
function
|
|
94
|
+
function focusToolbarItem(item: HTMLElement | null) {
|
|
95
|
+
if (!toolbarEl || !item) return;
|
|
96
|
+
|
|
97
|
+
for (const focusableItem of queryFocusable(toolbarEl)) {
|
|
98
|
+
focusableItem.setAttribute('tabindex', focusableItem === item ? '0' : '-1');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
item.focus();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function openLinkInput(
|
|
105
|
+
currentLink: string | null,
|
|
106
|
+
options: {
|
|
107
|
+
origin?: LinkRequestOrigin;
|
|
108
|
+
returnFocusTo?: HTMLElement | null;
|
|
109
|
+
} = {}
|
|
110
|
+
) {
|
|
70
111
|
const sel = window.getSelection();
|
|
71
112
|
if (sel && sel.rangeCount > 0) {
|
|
72
113
|
savedSelection = sel.getRangeAt(0).cloneRange();
|
|
73
114
|
}
|
|
115
|
+
linkRequestOrigin = options.origin ?? 'toolbar';
|
|
116
|
+
linkReturnFocusEl = options.returnFocusTo ?? null;
|
|
74
117
|
linkUrl = currentLink ?? 'https://';
|
|
75
118
|
showLinkInput = true;
|
|
76
119
|
requestAnimationFrame(() => {
|
|
@@ -79,13 +122,7 @@
|
|
|
79
122
|
});
|
|
80
123
|
}
|
|
81
124
|
|
|
82
|
-
function
|
|
83
|
-
showLinkInput = false;
|
|
84
|
-
linkUrl = '';
|
|
85
|
-
savedSelection = null;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function restoreSelection() {
|
|
125
|
+
function restoreSavedSelection() {
|
|
89
126
|
if (savedSelection) {
|
|
90
127
|
const sel = window.getSelection();
|
|
91
128
|
if (sel) {
|
|
@@ -94,6 +131,35 @@
|
|
|
94
131
|
}
|
|
95
132
|
}
|
|
96
133
|
}
|
|
134
|
+
|
|
135
|
+
function cancelLinkInput() {
|
|
136
|
+
if (linkRequestOrigin === 'content') {
|
|
137
|
+
restoreSavedSelection();
|
|
138
|
+
ctx.contentEl?.focus();
|
|
139
|
+
} else {
|
|
140
|
+
focusToolbarItem(linkReturnFocusEl);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
closeLinkInput();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function applyLink() {
|
|
147
|
+
if (linkUrl) {
|
|
148
|
+
restoreSavedSelection();
|
|
149
|
+
ctx.insertLink(linkUrl);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
closeLinkInput();
|
|
153
|
+
ctx.contentEl?.focus();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function closeLinkInput() {
|
|
157
|
+
showLinkInput = false;
|
|
158
|
+
linkUrl = '';
|
|
159
|
+
savedSelection = null;
|
|
160
|
+
linkReturnFocusEl = null;
|
|
161
|
+
linkRequestOrigin = 'toolbar';
|
|
162
|
+
}
|
|
97
163
|
</script>
|
|
98
164
|
|
|
99
165
|
<div
|
|
@@ -321,13 +387,11 @@
|
|
|
321
387
|
title="Link (Ctrl+K)"
|
|
322
388
|
tabindex={-1}
|
|
323
389
|
onmousedown={(e) => e.preventDefault()}
|
|
324
|
-
onclick={() =>
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
}
|
|
330
|
-
}}
|
|
390
|
+
onclick={(event) =>
|
|
391
|
+
openLinkInput(ctx.currentLink, {
|
|
392
|
+
origin: 'toolbar',
|
|
393
|
+
returnFocusTo: event.currentTarget as HTMLElement
|
|
394
|
+
})}
|
|
331
395
|
>
|
|
332
396
|
<svg
|
|
333
397
|
width="16"
|
|
@@ -382,19 +446,16 @@
|
|
|
382
446
|
<input
|
|
383
447
|
bind:value={linkUrl}
|
|
384
448
|
bind:this={linkInputEl}
|
|
449
|
+
aria-label="Link URL"
|
|
385
450
|
data-part="linkInput"
|
|
386
451
|
type="url"
|
|
387
452
|
placeholder="https://example.com"
|
|
388
453
|
onkeydown={(e) => {
|
|
389
454
|
if (e.key === 'Enter') {
|
|
390
455
|
e.preventDefault();
|
|
391
|
-
|
|
392
|
-
restoreSelection();
|
|
393
|
-
ctx.insertLink(linkUrl);
|
|
394
|
-
}
|
|
395
|
-
closeLinkInput();
|
|
456
|
+
applyLink();
|
|
396
457
|
} else if (e.key === 'Escape') {
|
|
397
|
-
|
|
458
|
+
cancelLinkInput();
|
|
398
459
|
}
|
|
399
460
|
}}
|
|
400
461
|
/>
|
|
@@ -403,20 +464,14 @@
|
|
|
403
464
|
size="sm"
|
|
404
465
|
type="button"
|
|
405
466
|
data-part="linkApply"
|
|
406
|
-
onclick={() =>
|
|
407
|
-
if (linkUrl) {
|
|
408
|
-
restoreSelection();
|
|
409
|
-
ctx.insertLink(linkUrl);
|
|
410
|
-
}
|
|
411
|
-
closeLinkInput();
|
|
412
|
-
}}>Apply</Button
|
|
467
|
+
onclick={() => applyLink()}>Apply</Button
|
|
413
468
|
>
|
|
414
469
|
<Button
|
|
415
470
|
variant="outline"
|
|
416
471
|
size="sm"
|
|
417
472
|
type="button"
|
|
418
473
|
data-part="linkCancel"
|
|
419
|
-
onclick={() =>
|
|
474
|
+
onclick={() => cancelLinkInput()}>Cancel</Button
|
|
420
475
|
>
|
|
421
476
|
</div>
|
|
422
477
|
{/if}
|
|
@@ -7,16 +7,28 @@
|
|
|
7
7
|
children: Snippet;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
let {
|
|
10
|
+
let {
|
|
11
|
+
orientation = 'vertical',
|
|
12
|
+
role: roleProp,
|
|
13
|
+
tabindex = 0,
|
|
14
|
+
'aria-label': ariaLabel,
|
|
15
|
+
'aria-labelledby': ariaLabelledBy,
|
|
16
|
+
class: className,
|
|
17
|
+
children,
|
|
18
|
+
...rest
|
|
19
|
+
}: Props = $props();
|
|
20
|
+
|
|
21
|
+
const role = $derived(roleProp ?? (ariaLabel || ariaLabelledBy ? 'region' : undefined));
|
|
11
22
|
</script>
|
|
12
23
|
|
|
13
24
|
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
14
25
|
<div
|
|
15
|
-
role
|
|
16
|
-
aria-label=
|
|
26
|
+
{role}
|
|
27
|
+
aria-label={ariaLabel}
|
|
28
|
+
aria-labelledby={ariaLabelledBy}
|
|
17
29
|
data-scroll-area
|
|
18
30
|
data-orientation={orientation}
|
|
19
|
-
tabindex
|
|
31
|
+
{tabindex}
|
|
20
32
|
class={className}
|
|
21
33
|
{...rest}
|
|
22
34
|
>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { fromAction } from 'svelte/attachments';
|
|
2
3
|
import type { Snippet } from 'svelte';
|
|
3
4
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
-
import {
|
|
5
|
-
import type { Placement } from '@dryui/primitives';
|
|
5
|
+
import { createAnchoredPopover, type Placement } from '@dryui/primitives';
|
|
6
6
|
import { getSelectCtx } from './context.svelte.js';
|
|
7
7
|
|
|
8
8
|
const OPTION_SELECTOR = '[role="option"]:not([data-disabled])';
|
|
@@ -24,30 +24,17 @@
|
|
|
24
24
|
|
|
25
25
|
const ctx = getSelectCtx();
|
|
26
26
|
|
|
27
|
-
let el = $state<HTMLDivElement>();
|
|
28
|
-
|
|
29
|
-
const anchor = createAnchorPosition(
|
|
30
|
-
() => ctx.triggerEl,
|
|
31
|
-
() => el ?? null,
|
|
32
|
-
{
|
|
33
|
-
get placement() {
|
|
34
|
-
return placement;
|
|
35
|
-
},
|
|
36
|
-
get offset() {
|
|
37
|
-
return offset;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
);
|
|
27
|
+
let el = $state<HTMLDivElement | null>(null);
|
|
41
28
|
|
|
42
|
-
|
|
43
|
-
|
|
29
|
+
function attachContent(node: HTMLDivElement) {
|
|
30
|
+
el = node;
|
|
44
31
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
}
|
|
32
|
+
return () => {
|
|
33
|
+
if (el === node) {
|
|
34
|
+
el = null;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
51
38
|
|
|
52
39
|
function getOptionItems(container: HTMLElement): HTMLElement[] {
|
|
53
40
|
return Array.from(container.querySelectorAll<HTMLElement>(OPTION_SELECTOR));
|
|
@@ -59,12 +46,14 @@
|
|
|
59
46
|
items[clamped]?.focus();
|
|
60
47
|
}
|
|
61
48
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
49
|
+
const popover = createAnchoredPopover({
|
|
50
|
+
triggerEl: () => ctx.triggerEl,
|
|
51
|
+
contentEl: () => el ?? null,
|
|
52
|
+
open: () => ctx.open,
|
|
53
|
+
placement: () => placement,
|
|
54
|
+
offset: () => offset,
|
|
55
|
+
onAfterShow: () => {
|
|
65
56
|
focusFirstSelectItem();
|
|
66
|
-
} else if (!ctx.open && el?.matches(':popover-open')) {
|
|
67
|
-
el.hidePopover();
|
|
68
57
|
}
|
|
69
58
|
});
|
|
70
59
|
|
|
@@ -127,7 +116,8 @@
|
|
|
127
116
|
</script>
|
|
128
117
|
|
|
129
118
|
<div
|
|
130
|
-
|
|
119
|
+
{@attach attachContent}
|
|
120
|
+
{@attach fromAction(popover.applyPosition, () => style)}
|
|
131
121
|
popover="auto"
|
|
132
122
|
role="listbox"
|
|
133
123
|
id={ctx.contentId}
|
|
@@ -156,7 +146,7 @@
|
|
|
156
146
|
margin: 0;
|
|
157
147
|
|
|
158
148
|
display: grid;
|
|
159
|
-
grid-template-columns: minmax(12rem, max-content);
|
|
149
|
+
grid-template-columns: minmax(max(12rem, anchor-size(inline)), max-content);
|
|
160
150
|
background: var(--dry-overlay-bg, var(--dry-color-bg-overlay));
|
|
161
151
|
border: 1px solid var(--dry-overlay-border, var(--dry-color-stroke-weak));
|
|
162
152
|
border-radius: var(--dry-overlay-radius, var(--dry-radius-md));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
2
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
-
import type
|
|
3
|
+
import { type Placement } from '@dryui/primitives';
|
|
4
4
|
interface Props extends HTMLAttributes<HTMLDivElement> {
|
|
5
5
|
placement?: Placement;
|
|
6
6
|
offset?: number;
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
const contentId = generateFormId('select-content');
|
|
40
40
|
|
|
41
41
|
let displayText = $state('');
|
|
42
|
+
let triggerEl = $state<HTMLElement | null>(null);
|
|
42
43
|
|
|
43
44
|
setSelectCtx({
|
|
44
45
|
get open() {
|
|
@@ -55,7 +56,12 @@
|
|
|
55
56
|
},
|
|
56
57
|
triggerId,
|
|
57
58
|
contentId,
|
|
58
|
-
triggerEl
|
|
59
|
+
get triggerEl() {
|
|
60
|
+
return triggerEl;
|
|
61
|
+
},
|
|
62
|
+
set triggerEl(element: HTMLElement | null) {
|
|
63
|
+
triggerEl = element;
|
|
64
|
+
},
|
|
59
65
|
show() {
|
|
60
66
|
if (!disabled) open = true;
|
|
61
67
|
},
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
import { observeInViewport, observePageVisibility } from '@dryui/primitives/internal/motion';
|
|
4
5
|
|
|
5
6
|
interface Props extends Omit<HTMLAttributes<HTMLSpanElement>, 'children'> {
|
|
6
7
|
color?: string;
|
|
@@ -17,27 +18,36 @@
|
|
|
17
18
|
}: Props = $props();
|
|
18
19
|
|
|
19
20
|
function setup(node: HTMLSpanElement) {
|
|
21
|
+
let onScreen = true;
|
|
22
|
+
let tabVisible = true;
|
|
23
|
+
|
|
24
|
+
const apply = () => {
|
|
25
|
+
if (onScreen && tabVisible) node.dataset.active = '';
|
|
26
|
+
else delete node.dataset.active;
|
|
27
|
+
};
|
|
28
|
+
|
|
20
29
|
$effect(() => {
|
|
21
30
|
node.style.setProperty('--dry-shimmer-color', color);
|
|
22
31
|
node.style.setProperty('--dry-shimmer-duration', `${duration}s`);
|
|
23
32
|
});
|
|
24
33
|
|
|
25
34
|
$effect(() => {
|
|
26
|
-
|
|
27
|
-
node
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
(entries) => {
|
|
32
|
-
for (const entry of entries) {
|
|
33
|
-
if (entry.isIntersecting) node.dataset.active = '';
|
|
34
|
-
else delete node.dataset.active;
|
|
35
|
-
}
|
|
35
|
+
const unsubscribeViewport = observeInViewport(
|
|
36
|
+
node,
|
|
37
|
+
(inView) => {
|
|
38
|
+
onScreen = inView;
|
|
39
|
+
apply();
|
|
36
40
|
},
|
|
37
41
|
{ rootMargin: '100px' }
|
|
38
42
|
);
|
|
39
|
-
|
|
40
|
-
|
|
43
|
+
const unsubscribeVisibility = observePageVisibility((visible) => {
|
|
44
|
+
tabVisible = visible;
|
|
45
|
+
apply();
|
|
46
|
+
});
|
|
47
|
+
return () => {
|
|
48
|
+
unsubscribeViewport();
|
|
49
|
+
unsubscribeVisibility();
|
|
50
|
+
};
|
|
41
51
|
});
|
|
42
52
|
}
|
|
43
53
|
</script>
|
|
@@ -79,6 +79,14 @@
|
|
|
79
79
|
justify-content: var(--dry-tabs-list-justify, start);
|
|
80
80
|
border-bottom: 1px solid var(--dry-color-stroke-weak);
|
|
81
81
|
gap: 0;
|
|
82
|
+
overflow-x: auto;
|
|
83
|
+
overscroll-behavior-x: contain;
|
|
84
|
+
scrollbar-width: none;
|
|
85
|
+
contain: inline-size;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
[data-tabs-list]::-webkit-scrollbar {
|
|
89
|
+
display: none;
|
|
82
90
|
}
|
|
83
91
|
|
|
84
92
|
[data-tabs-list][data-orientation='vertical'] {
|
|
@@ -86,5 +94,9 @@
|
|
|
86
94
|
grid-auto-columns: initial;
|
|
87
95
|
border-bottom: none;
|
|
88
96
|
border-right: 1px solid var(--dry-color-stroke-weak);
|
|
97
|
+
overflow-x: visible;
|
|
98
|
+
overflow-y: auto;
|
|
99
|
+
overscroll-behavior-x: auto;
|
|
100
|
+
overscroll-behavior-y: contain;
|
|
89
101
|
}
|
|
90
102
|
</style>
|
|
@@ -25,12 +25,9 @@
|
|
|
25
25
|
</script>
|
|
26
26
|
|
|
27
27
|
<label
|
|
28
|
-
role="option"
|
|
29
28
|
data-transfer-item
|
|
30
29
|
data-disabled={disabled ? '' : undefined}
|
|
31
30
|
data-selected={isSelected ? '' : undefined}
|
|
32
|
-
aria-selected={isSelected}
|
|
33
|
-
aria-disabled={disabled}
|
|
34
31
|
{...rest}
|
|
35
32
|
>
|
|
36
33
|
<Checkbox checked={isSelected} {disabled} onchange={toggle} size="sm" />
|
|
@@ -58,9 +58,8 @@
|
|
|
58
58
|
{/if}
|
|
59
59
|
|
|
60
60
|
<div
|
|
61
|
-
role="
|
|
61
|
+
role="group"
|
|
62
62
|
aria-label={title ?? (type === 'source' ? 'Available items' : 'Selected items')}
|
|
63
|
-
aria-multiselectable="true"
|
|
64
63
|
data-transfer-list
|
|
65
64
|
data-type={type}
|
|
66
65
|
{...rest}
|
|
@@ -88,14 +87,10 @@
|
|
|
88
87
|
{@render content({ items: filterItems(items) })}
|
|
89
88
|
{:else}
|
|
90
89
|
{#each filterItems(items) as item (item.key)}
|
|
91
|
-
<!-- svelte-ignore a11y_no_noninteractive_element_to_interactive_role -->
|
|
92
90
|
<label
|
|
93
|
-
role="option"
|
|
94
91
|
data-transfer-item
|
|
95
92
|
data-disabled={item.disabled ? '' : undefined}
|
|
96
93
|
data-selected={selectedSet.has(item.key) ? '' : undefined}
|
|
97
|
-
aria-selected={selectedSet.has(item.key)}
|
|
98
|
-
aria-disabled={item.disabled ?? false}
|
|
99
94
|
>
|
|
100
95
|
<Checkbox
|
|
101
96
|
checked={selectedSet.has(item.key)}
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
interface TreeContext {
|
|
2
|
-
readonly expandedItems:
|
|
2
|
+
readonly expandedItems: ReadonlySet<string>;
|
|
3
3
|
readonly selectedItem: string | null;
|
|
4
|
+
readonly focusedItem: string | null;
|
|
4
5
|
toggleItem: (id: string) => void;
|
|
5
6
|
expandItem: (id: string) => void;
|
|
6
7
|
collapseItem: (id: string) => void;
|
|
7
8
|
selectItem: (id: string) => void;
|
|
9
|
+
setFocusedItem: (id: string) => void;
|
|
10
|
+
registerBranch: (id: string) => void;
|
|
11
|
+
unregisterBranch: (id: string) => void;
|
|
8
12
|
isExpanded: (id: string) => boolean;
|
|
9
13
|
isSelected: (id: string) => boolean;
|
|
14
|
+
isFocused: (id: string) => boolean;
|
|
15
|
+
hasChildren: (id: string) => boolean;
|
|
10
16
|
}
|
|
11
17
|
export declare const setTreeCtx: (ctx: TreeContext) => TreeContext, getTreeCtx: () => TreeContext;
|
|
12
18
|
interface TreeItemContext {
|