sv5ui 2.0.0 → 2.1.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.svelte +11 -0
- package/dist/Badge/badge.types.d.ts +1 -1
- package/dist/Calendar/Calendar.svelte +14 -1
- package/dist/Collapsible/collapsible.types.d.ts +1 -1
- package/dist/Command/command.types.d.ts +4 -2
- package/dist/Command/index.d.ts +1 -1
- package/dist/ContextMenu/ContextMenu.svelte +1 -1
- package/dist/Drawer/Drawer.svelte +4 -2
- package/dist/Drawer/DrawerTriggerTestWrapper.svelte +10 -0
- package/dist/Drawer/DrawerTriggerTestWrapper.svelte.d.ts +18 -0
- package/dist/Drawer/drawer.types.d.ts +13 -2
- package/dist/Editor/Editor.svelte +85 -61
- package/dist/Editor/SlashPopup.svelte +8 -1
- package/dist/Editor/SlashPopup.svelte.d.ts +2 -0
- package/dist/Editor/editor.extensions.d.ts +1 -1
- package/dist/Editor/editor.extensions.js +25 -16
- package/dist/Editor/editor.schemas.d.ts +1 -0
- package/dist/Editor/editor.schemas.js +24 -0
- package/dist/Editor/editor.slash.svelte.d.ts +0 -9
- package/dist/Editor/editor.slash.svelte.js +33 -7
- package/dist/Editor/editor.suggestion.js +23 -0
- package/dist/Editor/editor.toolbar.js +0 -8
- package/dist/Editor/editor.types.d.ts +20 -0
- package/dist/Editor/editor.variants.d.ts +0 -5
- package/dist/Editor/editor.variants.js +0 -15
- package/dist/Editor/index.d.ts +6 -4
- package/dist/Editor/index.js +6 -4
- package/dist/FileUpload/FileUpload.svelte +7 -0
- package/dist/Icon/icon.types.d.ts +1 -1
- package/dist/Input/index.d.ts +1 -1
- package/dist/Modal/Modal.svelte +4 -2
- package/dist/Modal/ModalTriggerTestWrapper.svelte +10 -0
- package/dist/Modal/ModalTriggerTestWrapper.svelte.d.ts +18 -0
- package/dist/Modal/modal.types.d.ts +13 -3
- package/dist/Pagination/pagination.types.d.ts +1 -1
- package/dist/Popover/Popover.svelte +1 -1
- package/dist/Popover/popover.types.d.ts +2 -0
- package/dist/Progress/Progress.svelte +14 -6
- package/dist/RadioGroup/RadioGroup.svelte +3 -1
- package/dist/Select/select.types.d.ts +1 -1
- package/dist/SelectMenu/SelectMenu.svelte +21 -5
- package/dist/SelectMenu/select-menu.types.d.ts +1 -1
- package/dist/Separator/separator.types.d.ts +1 -1
- package/dist/Skeleton/Skeleton.svelte +3 -5
- package/dist/Slideover/Slideover.svelte +4 -2
- package/dist/Slideover/SlideoverTriggerTestWrapper.svelte +10 -0
- package/dist/Slideover/SlideoverTriggerTestWrapper.svelte.d.ts +18 -0
- package/dist/Slideover/slideover.types.d.ts +13 -3
- package/dist/Stepper/Stepper.svelte +1 -3
- package/dist/Switch/Switch.svelte +12 -17
- package/dist/Table/table.utils.d.ts +7 -4
- package/dist/Table/table.utils.js +26 -25
- package/dist/Tabs/tabs.types.d.ts +1 -1
- package/dist/ThemeModeButton/ThemeModeButton.svelte +4 -3
- package/dist/Tooltip/Tooltip.svelte +1 -1
- package/dist/Tooltip/tooltip.types.d.ts +2 -0
- package/package.json +1 -1
|
@@ -9,10 +9,6 @@ function defaultPromptUrl(opts) {
|
|
|
9
9
|
return Promise.resolve(null);
|
|
10
10
|
return Promise.resolve(window.prompt(opts.title, opts.initialValue ?? opts.placeholder ?? ''));
|
|
11
11
|
}
|
|
12
|
-
/**
|
|
13
|
-
* Returns the built-in slash command list, optionally including media
|
|
14
|
-
* commands based on which features are enabled in the host Editor.
|
|
15
|
-
*/
|
|
16
12
|
export function buildDefaultSlashCommands(ctx = {}) {
|
|
17
13
|
const commands = [
|
|
18
14
|
{
|
|
@@ -143,6 +139,7 @@ export function buildDefaultSlashCommands(ctx = {}) {
|
|
|
143
139
|
}
|
|
144
140
|
return commands;
|
|
145
141
|
}
|
|
142
|
+
let slashSeq = 0;
|
|
146
143
|
function substringFilter(commands, query) {
|
|
147
144
|
const q = query.trim().toLowerCase();
|
|
148
145
|
if (!q)
|
|
@@ -163,6 +160,10 @@ function buildSuggestionRender() {
|
|
|
163
160
|
onStart: (props) => {
|
|
164
161
|
if (typeof document === 'undefined')
|
|
165
162
|
return;
|
|
163
|
+
const seq = ++slashSeq;
|
|
164
|
+
const listboxId = `sv5ui-slash-listbox-${seq}`;
|
|
165
|
+
const optionIdPrefix = `sv5ui-slash-${seq}-`;
|
|
166
|
+
const editorDom = props.editor.view.dom;
|
|
166
167
|
const container = document.createElement('div');
|
|
167
168
|
container.setAttribute('data-editor-slash-container', '');
|
|
168
169
|
container.style.cssText = 'position:absolute;top:0;left:0;z-index:50;';
|
|
@@ -170,6 +171,8 @@ function buildSuggestionRender() {
|
|
|
170
171
|
const state = $state({
|
|
171
172
|
items: props.items,
|
|
172
173
|
selectedIndex: 0,
|
|
174
|
+
listboxId,
|
|
175
|
+
optionIdPrefix,
|
|
173
176
|
onPick: (i) => {
|
|
174
177
|
const cmd = state.items[i];
|
|
175
178
|
if (!cmd)
|
|
@@ -181,7 +184,26 @@ function buildSuggestionRender() {
|
|
|
181
184
|
target: container,
|
|
182
185
|
props: state
|
|
183
186
|
});
|
|
184
|
-
|
|
187
|
+
editorDom.setAttribute('aria-controls', listboxId);
|
|
188
|
+
editorDom.setAttribute('aria-expanded', 'true');
|
|
189
|
+
const stopActiveDescendant = $effect.root(() => {
|
|
190
|
+
$effect(() => {
|
|
191
|
+
if (state.items.length > 0) {
|
|
192
|
+
editorDom.setAttribute('aria-activedescendant', `${optionIdPrefix}${state.selectedIndex}`);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
editorDom.removeAttribute('aria-activedescendant');
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
handle = {
|
|
200
|
+
container,
|
|
201
|
+
component,
|
|
202
|
+
state,
|
|
203
|
+
cleanup: null,
|
|
204
|
+
editorDom,
|
|
205
|
+
stopActiveDescendant
|
|
206
|
+
};
|
|
185
207
|
const rect = props.clientRect?.();
|
|
186
208
|
if (rect) {
|
|
187
209
|
const virtualEl = { getBoundingClientRect: () => rect };
|
|
@@ -224,6 +246,12 @@ function buildSuggestionRender() {
|
|
|
224
246
|
if (!handle)
|
|
225
247
|
return;
|
|
226
248
|
handle.cleanup?.();
|
|
249
|
+
handle.stopActiveDescendant?.();
|
|
250
|
+
if (handle.editorDom) {
|
|
251
|
+
handle.editorDom.removeAttribute('aria-controls');
|
|
252
|
+
handle.editorDom.removeAttribute('aria-expanded');
|
|
253
|
+
handle.editorDom.removeAttribute('aria-activedescendant');
|
|
254
|
+
}
|
|
227
255
|
unmount(handle.component);
|
|
228
256
|
handle.container.remove();
|
|
229
257
|
handle = null;
|
|
@@ -262,8 +290,6 @@ export function buildSlashExtension(commands, trigger = '/') {
|
|
|
262
290
|
allowSpaces: false,
|
|
263
291
|
items: ({ query }) => substringFilter(commands, query),
|
|
264
292
|
render: buildSuggestionRender,
|
|
265
|
-
// Tiptap merges `suggestion` shallowly when an extension is .configure()'d,
|
|
266
|
-
// so the command default in addOptions gets overwritten. Include it here.
|
|
267
293
|
command: ({ editor, range, props }) => {
|
|
268
294
|
editor.chain().focus().deleteRange(range).run();
|
|
269
295
|
props.run({ editor });
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { computePosition, flip, shift, offset, autoUpdate } from '@floating-ui/dom';
|
|
2
|
+
let mentionSeq = 0;
|
|
2
3
|
function createPopup() {
|
|
3
4
|
const popup = document.createElement('div');
|
|
4
5
|
popup.setAttribute('data-editor-mention-popup', '');
|
|
@@ -25,6 +26,9 @@ function renderItems(state) {
|
|
|
25
26
|
row.type = 'button';
|
|
26
27
|
row.setAttribute('data-mention-item', '');
|
|
27
28
|
row.setAttribute('data-index', String(i));
|
|
29
|
+
row.setAttribute('role', 'option');
|
|
30
|
+
row.id = `${state.optionIdPrefix}${i}`;
|
|
31
|
+
row.setAttribute('aria-selected', i === state.selectedIndex ? 'true' : 'false');
|
|
28
32
|
row.className = [
|
|
29
33
|
'flex w-full items-center gap-2 px-3 py-1.5 text-sm text-start',
|
|
30
34
|
'hover:bg-surface-container-high',
|
|
@@ -49,6 +53,9 @@ function renderItems(state) {
|
|
|
49
53
|
});
|
|
50
54
|
state.listEl?.appendChild(row);
|
|
51
55
|
});
|
|
56
|
+
if (state.editorDom && state.items.length > 0) {
|
|
57
|
+
state.editorDom.setAttribute('aria-activedescendant', `${state.optionIdPrefix}${state.selectedIndex}`);
|
|
58
|
+
}
|
|
52
59
|
}
|
|
53
60
|
export function buildMentionSuggestion(options) {
|
|
54
61
|
return {
|
|
@@ -66,17 +73,28 @@ export function buildMentionSuggestion(options) {
|
|
|
66
73
|
onStart: (props) => {
|
|
67
74
|
if (typeof document === 'undefined')
|
|
68
75
|
return;
|
|
76
|
+
const seq = ++mentionSeq;
|
|
77
|
+
const listboxId = `sv5ui-mention-listbox-${seq}`;
|
|
78
|
+
const optionIdPrefix = `sv5ui-mention-${seq}-`;
|
|
79
|
+
const editorDom = props.editor.view.dom;
|
|
69
80
|
const popupEl = createPopup();
|
|
70
81
|
const listEl = document.createElement('div');
|
|
71
82
|
listEl.setAttribute('role', 'listbox');
|
|
83
|
+
listEl.id = listboxId;
|
|
84
|
+
listEl.setAttribute('aria-label', 'Mentions');
|
|
72
85
|
popupEl.appendChild(listEl);
|
|
73
86
|
document.body.appendChild(popupEl);
|
|
87
|
+
editorDom.setAttribute('aria-controls', listboxId);
|
|
88
|
+
editorDom.setAttribute('aria-expanded', 'true');
|
|
74
89
|
state = {
|
|
75
90
|
items: props.items,
|
|
76
91
|
selectedIndex: 0,
|
|
77
92
|
popupEl,
|
|
78
93
|
listEl,
|
|
79
94
|
cleanup: null,
|
|
95
|
+
editorDom,
|
|
96
|
+
listboxId,
|
|
97
|
+
optionIdPrefix,
|
|
80
98
|
pickItem: (i) => {
|
|
81
99
|
const it = state?.items[i];
|
|
82
100
|
if (!it)
|
|
@@ -134,6 +152,11 @@ export function buildMentionSuggestion(options) {
|
|
|
134
152
|
onExit: () => {
|
|
135
153
|
state?.cleanup?.();
|
|
136
154
|
state?.popupEl?.remove();
|
|
155
|
+
if (state?.editorDom) {
|
|
156
|
+
state.editorDom.removeAttribute('aria-controls');
|
|
157
|
+
state.editorDom.removeAttribute('aria-expanded');
|
|
158
|
+
state.editorDom.removeAttribute('aria-activedescendant');
|
|
159
|
+
}
|
|
137
160
|
state = null;
|
|
138
161
|
}
|
|
139
162
|
};
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
// ---------------------------------------------------------------------------
|
|
2
|
-
// Fallback `run` handlers below (link/image/youtube) use window.prompt because
|
|
3
|
-
// they need to gather a URL. Editor.svelte intercepts these actions and routes
|
|
4
|
-
// them through the EditorUrlPrompt modal instead. The fallbacks only execute
|
|
5
|
-
// when a consumer calls `api.run('link'|'image'|'youtube')` directly — at
|
|
6
|
-
// which point we have no modal mount point, so window.prompt is the simplest
|
|
7
|
-
// non-UI fallback.
|
|
8
|
-
// ---------------------------------------------------------------------------
|
|
9
1
|
function promptForLink(editor) {
|
|
10
2
|
const previous = editor.getAttributes('link').href ?? 'https://';
|
|
11
3
|
const url = typeof window === 'undefined' ? null : window.prompt('URL', previous);
|
|
@@ -195,10 +195,20 @@ export interface EditorProps extends Omit<HTMLAttributes<HTMLElement>, 'class' |
|
|
|
195
195
|
* Bindable content. The runtime type depends on `output`:
|
|
196
196
|
* - `output: 'html'` (default) → `string` of HTML
|
|
197
197
|
* - `output: 'json'` → `EditorJSON` document
|
|
198
|
+
*
|
|
199
|
+
* Security: the serialized value is structurally validated by the editor
|
|
200
|
+
* schema (unknown tags, attributes, and event handlers are dropped), but it
|
|
201
|
+
* is NOT sanitizer-clean. It can still contain arbitrary image `src` values
|
|
202
|
+
* (including `data:` URIs from pasted content). Sanitize the HTML output
|
|
203
|
+
* (e.g. with DOMPurify) before rendering it as raw markup elsewhere.
|
|
198
204
|
*/
|
|
199
205
|
value?: string | EditorJSON;
|
|
200
206
|
/**
|
|
201
207
|
* Serialization format used for `value` and `onValueChange`.
|
|
208
|
+
*
|
|
209
|
+
* Read once when the editor mounts (it also decides whether the Markdown
|
|
210
|
+
* extension is loaded). Changing it on an existing editor has no effect —
|
|
211
|
+
* re-key the component (e.g. `{#key output}`) to switch formats.
|
|
202
212
|
* @default 'html'
|
|
203
213
|
*/
|
|
204
214
|
output?: EditorOutput;
|
|
@@ -257,8 +267,18 @@ export interface EditorProps extends Omit<HTMLAttributes<HTMLElement>, 'class' |
|
|
|
257
267
|
* must return the resolved URL to insert as `<img src=...>`. When
|
|
258
268
|
* omitted and `image` is `true`, images can only be inserted via URL
|
|
259
269
|
* prompt (toolbar).
|
|
270
|
+
*
|
|
271
|
+
* The returned URL is validated before insertion: relative URLs,
|
|
272
|
+
* `http(s)`, and raster `data:image/*` URIs are allowed; `javascript:`,
|
|
273
|
+
* `data:text/*`, and `data:image/svg+xml` are rejected (the image is not
|
|
274
|
+
* inserted and a warning is logged).
|
|
260
275
|
*/
|
|
261
276
|
onImageUpload?: (file: File) => Promise<string>;
|
|
277
|
+
/**
|
|
278
|
+
* Called when `onImageUpload` rejects. When omitted, the error is logged to
|
|
279
|
+
* the console. Use this to surface upload failures to the user.
|
|
280
|
+
*/
|
|
281
|
+
onImageUploadError?: (error: unknown) => void;
|
|
262
282
|
/**
|
|
263
283
|
* Enable tables. Adds the `table` toolbar action which opens a
|
|
264
284
|
* dimension picker (rows × columns) and inserts a new table.
|
|
@@ -52,7 +52,6 @@ export declare const editorVariants: import("tailwind-variants").TVReturnType<{
|
|
|
52
52
|
}, {
|
|
53
53
|
root: string[];
|
|
54
54
|
toolbar: string[];
|
|
55
|
-
toolbarGroup: string;
|
|
56
55
|
toolbarButton: string[];
|
|
57
56
|
toolbarSeparator: string;
|
|
58
57
|
content: string[];
|
|
@@ -112,7 +111,6 @@ export declare const editorVariants: import("tailwind-variants").TVReturnType<{
|
|
|
112
111
|
}, {
|
|
113
112
|
root: string[];
|
|
114
113
|
toolbar: string[];
|
|
115
|
-
toolbarGroup: string;
|
|
116
114
|
toolbarButton: string[];
|
|
117
115
|
toolbarSeparator: string;
|
|
118
116
|
content: string[];
|
|
@@ -172,7 +170,6 @@ export declare const editorVariants: import("tailwind-variants").TVReturnType<{
|
|
|
172
170
|
}, {
|
|
173
171
|
root: string[];
|
|
174
172
|
toolbar: string[];
|
|
175
|
-
toolbarGroup: string;
|
|
176
173
|
toolbarButton: string[];
|
|
177
174
|
toolbarSeparator: string;
|
|
178
175
|
content: string[];
|
|
@@ -236,7 +233,6 @@ export declare const editorDefaults: {
|
|
|
236
233
|
}, {
|
|
237
234
|
root: string[];
|
|
238
235
|
toolbar: string[];
|
|
239
|
-
toolbarGroup: string;
|
|
240
236
|
toolbarButton: string[];
|
|
241
237
|
toolbarSeparator: string;
|
|
242
238
|
content: string[];
|
|
@@ -296,7 +292,6 @@ export declare const editorDefaults: {
|
|
|
296
292
|
}, {
|
|
297
293
|
root: string[];
|
|
298
294
|
toolbar: string[];
|
|
299
|
-
toolbarGroup: string;
|
|
300
295
|
toolbarButton: string[];
|
|
301
296
|
toolbarSeparator: string;
|
|
302
297
|
content: string[];
|
|
@@ -13,7 +13,6 @@ export const editorVariants = tv({
|
|
|
13
13
|
'bg-surface-container-low',
|
|
14
14
|
'rounded-t-lg p-1.5'
|
|
15
15
|
],
|
|
16
|
-
toolbarGroup: 'flex items-center gap-0.5',
|
|
17
16
|
toolbarButton: [
|
|
18
17
|
'inline-flex items-center justify-center rounded text-on-surface-variant',
|
|
19
18
|
'hover:bg-surface-container-high hover:text-on-surface',
|
|
@@ -25,7 +24,6 @@ export const editorVariants = tv({
|
|
|
25
24
|
toolbarSeparator: 'mx-1 h-5 w-px shrink-0 bg-outline-variant',
|
|
26
25
|
content: [
|
|
27
26
|
'text-on-surface',
|
|
28
|
-
// ----- Inner ProseMirror element (the actual contenteditable) -----
|
|
29
27
|
'[&_.ProseMirror]:outline-none',
|
|
30
28
|
'[&_.ProseMirror]:focus:outline-none',
|
|
31
29
|
'[&_.ProseMirror]:focus-visible:outline-none',
|
|
@@ -33,13 +31,11 @@ export const editorVariants = tv({
|
|
|
33
31
|
'[&_.ProseMirror]:min-h-32',
|
|
34
32
|
'[&_.ProseMirror]:whitespace-pre-wrap',
|
|
35
33
|
'[&_.ProseMirror]:break-words',
|
|
36
|
-
// ----- Placeholder (when editor is empty) -----
|
|
37
34
|
'[&_.is-editor-empty]:before:content-[attr(data-placeholder)]',
|
|
38
35
|
'[&_.is-editor-empty]:before:text-on-surface-variant/60',
|
|
39
36
|
'[&_.is-editor-empty]:before:pointer-events-none',
|
|
40
37
|
'[&_.is-editor-empty]:before:float-left',
|
|
41
38
|
'[&_.is-editor-empty]:before:h-0',
|
|
42
|
-
// ----- Content typography (lightweight in-house prose) -----
|
|
43
39
|
'[&_p]:my-2 [&_p:first-child]:mt-0 [&_p:last-child]:mb-0',
|
|
44
40
|
'[&_h1]:text-2xl [&_h1]:font-bold [&_h1]:my-3',
|
|
45
41
|
'[&_h2]:text-xl [&_h2]:font-bold [&_h2]:my-3',
|
|
@@ -56,17 +52,13 @@ export const editorVariants = tv({
|
|
|
56
52
|
'[&_hr]:border-outline-variant [&_hr]:my-4',
|
|
57
53
|
'[&_strong]:font-semibold',
|
|
58
54
|
'[&_em]:italic',
|
|
59
|
-
// ----- Image -----
|
|
60
55
|
'[&_img]:max-w-full [&_img]:h-auto [&_img]:rounded-md [&_img]:my-2',
|
|
61
|
-
// ----- YouTube embed (responsive 16:9) -----
|
|
62
56
|
'[&_iframe[src*="youtube"]]:aspect-video [&_iframe[src*="youtube"]]:w-full [&_iframe[src*="youtube"]]:rounded-md [&_iframe[src*="youtube"]]:my-3 [&_iframe[src*="youtube"]]:border-0',
|
|
63
|
-
// ----- Mention chip -----
|
|
64
57
|
'[&_.sv5ui-editor-mention]:inline-flex [&_.sv5ui-editor-mention]:items-center',
|
|
65
58
|
'[&_.sv5ui-editor-mention]:rounded [&_.sv5ui-editor-mention]:bg-primary-container/60',
|
|
66
59
|
'[&_.sv5ui-editor-mention]:text-on-primary-container',
|
|
67
60
|
'[&_.sv5ui-editor-mention]:px-1.5 [&_.sv5ui-editor-mention]:py-0.5',
|
|
68
61
|
'[&_.sv5ui-editor-mention]:text-sm [&_.sv5ui-editor-mention]:font-medium',
|
|
69
|
-
// ----- Table -----
|
|
70
62
|
'[&_.tableWrapper]:my-3 [&_.tableWrapper]:overflow-x-auto',
|
|
71
63
|
'[&_table]:w-full [&_table]:border-collapse [&_table]:table-fixed',
|
|
72
64
|
'[&_table]:border [&_table]:border-outline-variant [&_table]:rounded-md',
|
|
@@ -77,12 +69,10 @@ export const editorVariants = tv({
|
|
|
77
69
|
'[&_td]:border [&_td]:border-outline-variant',
|
|
78
70
|
'[&_td]:px-2 [&_td]:py-1.5 [&_td]:align-top',
|
|
79
71
|
'[&_td]:relative [&_td]:min-w-16',
|
|
80
|
-
// Tiptap selectedCell highlight + column resize handle
|
|
81
72
|
'[&_.selectedCell]:bg-primary/10',
|
|
82
73
|
'[&_.column-resize-handle]:absolute [&_.column-resize-handle]:top-0 [&_.column-resize-handle]:bottom-[-2px]',
|
|
83
74
|
'[&_.column-resize-handle]:-right-0.5 [&_.column-resize-handle]:w-1',
|
|
84
75
|
'[&_.column-resize-handle]:bg-primary/40 [&_.column-resize-handle]:pointer-events-none',
|
|
85
|
-
// ----- Selection styling -----
|
|
86
76
|
'[&_::selection]:bg-primary/20'
|
|
87
77
|
],
|
|
88
78
|
footer: [
|
|
@@ -94,11 +84,6 @@ export const editorVariants = tv({
|
|
|
94
84
|
],
|
|
95
85
|
countLabel: 'tabular-nums',
|
|
96
86
|
bubbleMenu: [
|
|
97
|
-
// ⚠️ Tiptap's BubbleMenu only sets `visibility:hidden` initially,
|
|
98
|
-
// leaving the element in normal flow (taking up space below the editor)
|
|
99
|
-
// until the user first selects text. Setting `position:absolute` upfront
|
|
100
|
-
// keeps it out of flow from mount — extension's Floating UI positioning
|
|
101
|
-
// then overrides left/top inline on selection.
|
|
102
87
|
'absolute top-0 left-0 z-50 invisible',
|
|
103
88
|
'flex items-center gap-0.5 p-1',
|
|
104
89
|
'rounded-lg border border-outline-variant bg-surface',
|
package/dist/Editor/index.d.ts
CHANGED
|
@@ -29,10 +29,12 @@
|
|
|
29
29
|
*
|
|
30
30
|
* (`npm add` / `yarn add` work equally well.)
|
|
31
31
|
*
|
|
32
|
-
* All 18 packages
|
|
33
|
-
* features
|
|
34
|
-
*
|
|
35
|
-
*
|
|
32
|
+
* All 18 packages must be installed even if you only enable a subset of
|
|
33
|
+
* features — missing peers fail at runtime with module-resolution errors.
|
|
34
|
+
* Most extensions are imported eagerly, but the heaviest optional ones load
|
|
35
|
+
* on demand: `tiptap-markdown` (only with `output="markdown"`) and the table
|
|
36
|
+
* packages (only with `tables`) are pulled in via dynamic `import()`, so your
|
|
37
|
+
* bundler code-splits them out of the base editor chunk when unused.
|
|
36
38
|
*
|
|
37
39
|
* ## Usage
|
|
38
40
|
*
|
package/dist/Editor/index.js
CHANGED
|
@@ -29,10 +29,12 @@
|
|
|
29
29
|
*
|
|
30
30
|
* (`npm add` / `yarn add` work equally well.)
|
|
31
31
|
*
|
|
32
|
-
* All 18 packages
|
|
33
|
-
* features
|
|
34
|
-
*
|
|
35
|
-
*
|
|
32
|
+
* All 18 packages must be installed even if you only enable a subset of
|
|
33
|
+
* features — missing peers fail at runtime with module-resolution errors.
|
|
34
|
+
* Most extensions are imported eagerly, but the heaviest optional ones load
|
|
35
|
+
* on demand: `tiptap-markdown` (only with `output="markdown"`) and the table
|
|
36
|
+
* packages (only with `tables`) are pulled in via dynamic `import()`, so your
|
|
37
|
+
* bundler code-splits them out of the base editor chunk when unused.
|
|
36
38
|
*
|
|
37
39
|
* ## Usage
|
|
38
40
|
*
|
|
@@ -3,7 +3,7 @@ import type { SVGAttributes } from 'svelte/elements';
|
|
|
3
3
|
import type { ClassNameValue } from 'tailwind-merge';
|
|
4
4
|
export interface IconProps extends Omit<IconifyProps, 'icon' | 'width' | 'height' | 'rotate' | 'flip' | 'class'>, Pick<SVGAttributes<SVGSVGElement>, 'role' | 'tabindex' | 'aria-label' | 'aria-labelledby' | 'aria-describedby' | 'aria-hidden' | 'onclick' | 'onkeydown' | 'onmouseenter' | 'onmouseleave' | 'onfocus' | 'onblur'> {
|
|
5
5
|
/** Custom data attributes are forwarded to the rendered `<svg>`. */
|
|
6
|
-
[key: `data-${string}`]:
|
|
6
|
+
[key: `data-${string}`]: string | number | boolean | null | undefined;
|
|
7
7
|
/**
|
|
8
8
|
* Icon name in Iconify format: "collection:icon-name"
|
|
9
9
|
* @example "lucide:home", "mdi:account", "heroicons:star"
|
package/dist/Input/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { default as Input } from './Input.svelte';
|
|
2
|
-
export type { InputProps } from './input.types.js';
|
|
2
|
+
export type { InputProps, InputValue } from './input.types.js';
|
package/dist/Modal/Modal.svelte
CHANGED
|
@@ -203,8 +203,10 @@
|
|
|
203
203
|
|
|
204
204
|
<Dialog.Root bind:open onOpenChange={handleOpenChange} {onOpenChangeComplete}>
|
|
205
205
|
{#if children}
|
|
206
|
-
<Dialog.Trigger
|
|
207
|
-
{
|
|
206
|
+
<Dialog.Trigger>
|
|
207
|
+
{#snippet child({ props })}
|
|
208
|
+
{@render children({ props })}
|
|
209
|
+
{/snippet}
|
|
208
210
|
</Dialog.Trigger>
|
|
209
211
|
{/if}
|
|
210
212
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Modal from './Modal.svelte'
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<Modal title="Trigger test" description="D">
|
|
6
|
+
{#snippet children({ props })}
|
|
7
|
+
<button data-testid="trigger" {...props}>Open</button>
|
|
8
|
+
{/snippet}
|
|
9
|
+
{#snippet body()}<p>Body</p>{/snippet}
|
|
10
|
+
</Modal>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
2
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
3
|
+
$$bindings?: Bindings;
|
|
4
|
+
} & Exports;
|
|
5
|
+
(internal: unknown, props: {
|
|
6
|
+
$$events?: Events;
|
|
7
|
+
$$slots?: Slots;
|
|
8
|
+
}): Exports & {
|
|
9
|
+
$set?: any;
|
|
10
|
+
$on?: any;
|
|
11
|
+
};
|
|
12
|
+
z_$$bindings?: Bindings;
|
|
13
|
+
}
|
|
14
|
+
declare const ModalTriggerTestWrapper: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
15
|
+
[evt: string]: CustomEvent<any>;
|
|
16
|
+
}, {}, {}, string>;
|
|
17
|
+
type ModalTriggerTestWrapper = InstanceType<typeof ModalTriggerTestWrapper>;
|
|
18
|
+
export default ModalTriggerTestWrapper;
|
|
@@ -101,10 +101,20 @@ export interface ModalProps extends RootProps, ContentProps {
|
|
|
101
101
|
*/
|
|
102
102
|
class?: ClassNameValue;
|
|
103
103
|
/**
|
|
104
|
-
*
|
|
105
|
-
*
|
|
104
|
+
* Trigger content. Spread the provided `props` onto your own focusable
|
|
105
|
+
* element (e.g. a `<Button>`) so the dialog's trigger ARIA and event
|
|
106
|
+
* handlers land on the real control instead of a nested wrapper button.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```svelte
|
|
110
|
+
* {#snippet children({ props })}
|
|
111
|
+
* <Button {...props}>Open</Button>
|
|
112
|
+
* {/snippet}
|
|
113
|
+
* ```
|
|
106
114
|
*/
|
|
107
|
-
children?: Snippet
|
|
115
|
+
children?: Snippet<[{
|
|
116
|
+
props: Record<string, unknown>;
|
|
117
|
+
}]>;
|
|
108
118
|
/**
|
|
109
119
|
* Custom content slot that replaces the entire default layout
|
|
110
120
|
* (header, body, footer). Title and description are rendered
|
|
@@ -41,7 +41,7 @@ export interface PaginationItemSlotProps {
|
|
|
41
41
|
*/
|
|
42
42
|
export interface PaginationProps extends Pick<PaginationRootProps, 'id' | 'style' | 'title' | 'role' | 'tabindex' | 'aria-label' | 'aria-labelledby' | 'aria-describedby' | 'onclick' | 'onkeydown' | 'onmouseenter' | 'onmouseleave' | 'onfocus' | 'onblur'> {
|
|
43
43
|
/** Custom data attributes are forwarded to the root element. */
|
|
44
|
-
[key: `data-${string}`]:
|
|
44
|
+
[key: `data-${string}`]: string | number | boolean | null | undefined;
|
|
45
45
|
/**
|
|
46
46
|
* Bindable reference to the root DOM element.
|
|
47
47
|
*/
|
|
@@ -5,6 +5,8 @@ import type { PopoverRootPropsWithoutHTML, PopoverContentPropsWithoutHTML, Popov
|
|
|
5
5
|
type RootProps = Pick<PopoverRootPropsWithoutHTML, 'open' | 'onOpenChange' | 'onOpenChangeComplete'>;
|
|
6
6
|
type ContentProps = Pick<PopoverContentPropsWithoutHTML, 'side' | 'sideOffset' | 'align' | 'alignOffset' | 'avoidCollisions' | 'collisionBoundary' | 'collisionPadding' | 'sticky' | 'hideWhenDetached' | 'trapFocus' | 'preventScroll' | 'onOpenAutoFocus' | 'onCloseAutoFocus' | 'onEscapeKeydown' | 'onInteractOutside' | 'forceMount'>;
|
|
7
7
|
export interface PopoverProps extends RootProps, ContentProps {
|
|
8
|
+
/** Custom data attributes are forwarded to the content element. */
|
|
9
|
+
[key: `data-${string}`]: string | number | boolean | null | undefined;
|
|
8
10
|
/**
|
|
9
11
|
* Bindable reference to the content DOM element.
|
|
10
12
|
*/
|
|
@@ -80,6 +80,19 @@
|
|
|
80
80
|
}
|
|
81
81
|
})
|
|
82
82
|
|
|
83
|
+
const stepClasses = $derived.by(() => {
|
|
84
|
+
const make = (step: 'active' | 'first' | 'last' | 'other') =>
|
|
85
|
+
progressVariants({ size, orientation, inverted, step }).step({
|
|
86
|
+
class: [config.slots.step, ui?.step]
|
|
87
|
+
})
|
|
88
|
+
return {
|
|
89
|
+
active: make('active'),
|
|
90
|
+
first: make('first'),
|
|
91
|
+
last: make('last'),
|
|
92
|
+
other: make('other')
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
|
|
83
96
|
const state = $derived(isIndeterminate ? 'indeterminate' : 'determinate')
|
|
84
97
|
</script>
|
|
85
98
|
|
|
@@ -108,12 +121,7 @@
|
|
|
108
121
|
{#if hasSteps && Array.isArray(max)}
|
|
109
122
|
<div class={classes.steps}>
|
|
110
123
|
{#each max as step, index (index)}
|
|
111
|
-
{@const stepClass =
|
|
112
|
-
size,
|
|
113
|
-
orientation,
|
|
114
|
-
inverted,
|
|
115
|
-
step: stepVariant(index)
|
|
116
|
-
}).step({ class: [config.slots.step, ui?.step] })}
|
|
124
|
+
{@const stepClass = stepClasses[stepVariant(index)]}
|
|
117
125
|
<div class={stepClass}>
|
|
118
126
|
{#if stepSlot}
|
|
119
127
|
{@render stepSlot({ step, index })}
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
const resolvedId = $derived(id ?? formFieldContext?.ariaId ?? autoId)
|
|
55
55
|
const resolvedName = $derived(name ?? formFieldContext?.name)
|
|
56
56
|
const isDisabled = $derived(disabled || loading)
|
|
57
|
+
const legendId = $derived(`${resolvedId}-legend`)
|
|
57
58
|
|
|
58
59
|
const ariaDescribedBy = $derived(
|
|
59
60
|
!formFieldContext
|
|
@@ -186,13 +187,14 @@
|
|
|
186
187
|
{orientation}
|
|
187
188
|
aria-describedby={ariaDescribedBy}
|
|
188
189
|
aria-invalid={hasError ? true : undefined}
|
|
190
|
+
aria-labelledby={legend && !legendSlot ? legendId : undefined}
|
|
189
191
|
class={layoutClasses.fieldset}
|
|
190
192
|
>
|
|
191
193
|
{#if legend || legendSlot}
|
|
192
194
|
{#if legendSlot}
|
|
193
195
|
{@render legendSlot({ legend })}
|
|
194
196
|
{:else}
|
|
195
|
-
<span class={layoutClasses.legend}>{legend}</span>
|
|
197
|
+
<span id={legendId} class={layoutClasses.legend}>{legend}</span>
|
|
196
198
|
{/if}
|
|
197
199
|
{/if}
|
|
198
200
|
|
|
@@ -105,7 +105,7 @@ type ContentProps = Pick<SelectContentPropsWithoutHTML, 'side' | 'sideOffset' |
|
|
|
105
105
|
type TriggerHTMLProps = Pick<SelectTriggerProps, 'style' | 'title' | 'role' | 'tabindex' | 'aria-label' | 'aria-labelledby' | 'onclick' | 'onkeydown' | 'onmouseenter' | 'onmouseleave' | 'onfocus' | 'onblur'>;
|
|
106
106
|
export interface SelectProps extends RootProps, ContentProps, TriggerHTMLProps {
|
|
107
107
|
/** Custom data attributes are forwarded to the trigger element. */
|
|
108
|
-
[key: `data-${string}`]:
|
|
108
|
+
[key: `data-${string}`]: string | number | boolean | null | undefined;
|
|
109
109
|
/**
|
|
110
110
|
* Bindable reference to the root DOM element.
|
|
111
111
|
*/
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
import Avatar from '../Avatar/Avatar.svelte'
|
|
24
24
|
import type { AvatarSize } from '../Avatar/avatar.types.js'
|
|
25
25
|
import { useFormField, useFormFieldEmit } from '../hooks/useFormField.svelte.js'
|
|
26
|
+
import { useDebounce } from '../hooks/useDebounce.svelte.js'
|
|
26
27
|
|
|
27
28
|
const config = getComponentConfig('selectMenu', selectMenuDefaults)
|
|
28
29
|
const icons = getComponentConfig('icons', iconsDefaults)
|
|
@@ -181,13 +182,28 @@
|
|
|
181
182
|
|
|
182
183
|
// ---- Search & filtering ----
|
|
183
184
|
let searchTerm = $state('')
|
|
185
|
+
let debouncedSearch = $state('')
|
|
186
|
+
const searchDebounce = useDebounce({ delay: 200 })
|
|
187
|
+
|
|
188
|
+
function setSearch(term: string) {
|
|
189
|
+
searchTerm = term
|
|
190
|
+
searchDebounce.run(() => {
|
|
191
|
+
debouncedSearch = term
|
|
192
|
+
})
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function resetSearch() {
|
|
196
|
+
searchDebounce.cancel()
|
|
197
|
+
searchTerm = ''
|
|
198
|
+
debouncedSearch = ''
|
|
199
|
+
}
|
|
184
200
|
|
|
185
201
|
const filteredItems = $derived(
|
|
186
|
-
ignoreFilter || !
|
|
202
|
+
ignoreFilter || !debouncedSearch.trim()
|
|
187
203
|
? combinedItems
|
|
188
204
|
: combinedItems.filter((item) => {
|
|
189
205
|
if ('type' in item) return true
|
|
190
|
-
const query =
|
|
206
|
+
const query = debouncedSearch.toLowerCase()
|
|
191
207
|
return filterFields.some((field) => {
|
|
192
208
|
const val = (item as unknown as Record<string, unknown>)[field]
|
|
193
209
|
return typeof val === 'string' && val.toLowerCase().includes(query)
|
|
@@ -256,7 +272,7 @@
|
|
|
256
272
|
}
|
|
257
273
|
|
|
258
274
|
emit.onChange()
|
|
259
|
-
|
|
275
|
+
resetSearch()
|
|
260
276
|
}
|
|
261
277
|
|
|
262
278
|
// ---- Leading / trailing ----
|
|
@@ -386,7 +402,7 @@
|
|
|
386
402
|
// ---- Event handlers (Nuxt UI v4 pattern) ----
|
|
387
403
|
function onUpdateOpen(val: boolean) {
|
|
388
404
|
if (!val) {
|
|
389
|
-
|
|
405
|
+
resetSearch()
|
|
390
406
|
emit.onBlur()
|
|
391
407
|
} else {
|
|
392
408
|
emit.onFocus()
|
|
@@ -450,7 +466,7 @@
|
|
|
450
466
|
autofocus
|
|
451
467
|
placeholder={searchPlaceholder}
|
|
452
468
|
value={searchTerm}
|
|
453
|
-
oninput={(e) => (
|
|
469
|
+
oninput={(e) => setSearch((e.currentTarget as HTMLInputElement).value)}
|
|
454
470
|
onkeydown={(e: KeyboardEvent) => {
|
|
455
471
|
if (e.key !== 'Enter') return
|
|
456
472
|
if (!showCreateItem) return
|
|
@@ -90,7 +90,7 @@ type ContentProps = Pick<ComboboxContentPropsWithoutHTML, 'side' | 'sideOffset'
|
|
|
90
90
|
type TriggerHTMLProps = Pick<ComboboxTriggerProps, 'style' | 'title' | 'role' | 'tabindex' | 'aria-label' | 'aria-labelledby' | 'onclick' | 'onkeydown' | 'onmouseenter' | 'onmouseleave' | 'onfocus' | 'onblur'>;
|
|
91
91
|
export interface SelectMenuProps extends ContentProps, TriggerHTMLProps {
|
|
92
92
|
/** Custom data attributes are forwarded to the trigger element. */
|
|
93
|
-
[key: `data-${string}`]:
|
|
93
|
+
[key: `data-${string}`]: string | number | boolean | null | undefined;
|
|
94
94
|
/**
|
|
95
95
|
* Bindable reference to the root DOM element.
|
|
96
96
|
*/
|
|
@@ -3,7 +3,7 @@ import type { ClassNameValue } from 'tailwind-merge';
|
|
|
3
3
|
import type { Separator } from 'bits-ui';
|
|
4
4
|
import type { SeparatorVariantProps, SeparatorSlots } from './separator.variants.js';
|
|
5
5
|
import type { AvatarProps } from '../Avatar/avatar.types.js';
|
|
6
|
-
export type SeparatorProps = Separator.RootProps & {
|
|
6
|
+
export type SeparatorProps = Omit<Separator.RootProps, 'class'> & {
|
|
7
7
|
/**
|
|
8
8
|
* Sets the color scheme applied to the separator.
|
|
9
9
|
* @default 'surface'
|