sv5ui 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/Banner/Banner.svelte +162 -0
  2. package/dist/Banner/Banner.svelte.d.ts +5 -0
  3. package/dist/Banner/banner.types.d.ts +148 -0
  4. package/dist/Banner/banner.types.js +1 -0
  5. package/dist/Banner/banner.variants.d.ts +293 -0
  6. package/dist/Banner/banner.variants.js +86 -0
  7. package/dist/Banner/index.d.ts +2 -0
  8. package/dist/Banner/index.js +1 -0
  9. package/dist/Editor/Editor.svelte +738 -0
  10. package/dist/Editor/Editor.svelte.d.ts +6 -0
  11. package/dist/Editor/EditorUrlPrompt.svelte +111 -0
  12. package/dist/Editor/EditorUrlPrompt.svelte.d.ts +15 -0
  13. package/dist/Editor/SlashPopup.svelte +67 -0
  14. package/dist/Editor/SlashPopup.svelte.d.ts +9 -0
  15. package/dist/Editor/editor.extensions.d.ts +23 -0
  16. package/dist/Editor/editor.extensions.js +123 -0
  17. package/dist/Editor/editor.schemas.d.ts +4 -0
  18. package/dist/Editor/editor.schemas.js +3 -0
  19. package/dist/Editor/editor.slash.svelte.d.ts +34 -0
  20. package/dist/Editor/editor.slash.svelte.js +273 -0
  21. package/dist/Editor/editor.suggestion.d.ts +7 -0
  22. package/dist/Editor/editor.suggestion.js +142 -0
  23. package/dist/Editor/editor.toolbar.d.ts +11 -0
  24. package/dist/Editor/editor.toolbar.js +212 -0
  25. package/dist/Editor/editor.types.d.ts +347 -0
  26. package/dist/Editor/editor.types.js +1 -0
  27. package/dist/Editor/editor.variants.d.ts +308 -0
  28. package/dist/Editor/editor.variants.js +150 -0
  29. package/dist/Editor/index.d.ts +53 -0
  30. package/dist/Editor/index.js +52 -0
  31. package/dist/Stepper/Stepper.svelte +292 -0
  32. package/dist/Stepper/Stepper.svelte.d.ts +5 -0
  33. package/dist/Stepper/index.d.ts +2 -0
  34. package/dist/Stepper/index.js +1 -0
  35. package/dist/Stepper/stepper.types.d.ts +223 -0
  36. package/dist/Stepper/stepper.types.js +1 -0
  37. package/dist/Stepper/stepper.variants.d.ts +428 -0
  38. package/dist/Stepper/stepper.variants.js +204 -0
  39. package/dist/index.d.ts +2 -0
  40. package/dist/index.js +2 -0
  41. package/package.json +97 -1
@@ -0,0 +1,7 @@
1
+ import type { SuggestionOptions } from '@tiptap/suggestion';
2
+ import type { MentionItem } from './editor.types.js';
3
+ interface BuildMentionSuggestionOptions {
4
+ onQuery: (query: string) => Promise<MentionItem[]>;
5
+ }
6
+ export declare function buildMentionSuggestion(options: BuildMentionSuggestionOptions): Omit<SuggestionOptions, 'editor'>;
7
+ export {};
@@ -0,0 +1,142 @@
1
+ import { computePosition, flip, shift, offset, autoUpdate } from '@floating-ui/dom';
2
+ function createPopup() {
3
+ const popup = document.createElement('div');
4
+ popup.setAttribute('data-editor-mention-popup', '');
5
+ popup.className = [
6
+ 'absolute z-50 min-w-44 max-w-72 overflow-hidden',
7
+ 'rounded-lg border border-outline-variant bg-surface shadow-lg',
8
+ 'py-1 max-h-64 overflow-y-auto'
9
+ ].join(' ');
10
+ return popup;
11
+ }
12
+ function renderItems(state) {
13
+ if (!state.listEl)
14
+ return;
15
+ state.listEl.innerHTML = '';
16
+ if (state.items.length === 0) {
17
+ const empty = document.createElement('div');
18
+ empty.className = 'px-3 py-2 text-sm text-on-surface-variant';
19
+ empty.textContent = 'No matches';
20
+ state.listEl.appendChild(empty);
21
+ return;
22
+ }
23
+ state.items.forEach((item, i) => {
24
+ const row = document.createElement('button');
25
+ row.type = 'button';
26
+ row.setAttribute('data-mention-item', '');
27
+ row.setAttribute('data-index', String(i));
28
+ row.className = [
29
+ 'flex w-full items-center gap-2 px-3 py-1.5 text-sm text-start',
30
+ 'hover:bg-surface-container-high',
31
+ i === state.selectedIndex
32
+ ? 'bg-primary-container text-on-primary-container'
33
+ : 'text-on-surface'
34
+ ].join(' ');
35
+ if (item.avatar) {
36
+ const img = document.createElement('img');
37
+ img.src = item.avatar;
38
+ img.alt = '';
39
+ img.className = 'size-5 rounded-full shrink-0 object-cover';
40
+ row.appendChild(img);
41
+ }
42
+ const label = document.createElement('span');
43
+ label.className = 'truncate';
44
+ label.textContent = item.label;
45
+ row.appendChild(label);
46
+ row.addEventListener('mousedown', (e) => {
47
+ e.preventDefault();
48
+ state.pickItem(i);
49
+ });
50
+ state.listEl?.appendChild(row);
51
+ });
52
+ }
53
+ export function buildMentionSuggestion(options) {
54
+ return {
55
+ items: async ({ query }) => {
56
+ try {
57
+ return await options.onQuery(query);
58
+ }
59
+ catch {
60
+ return [];
61
+ }
62
+ },
63
+ render: () => {
64
+ let state = null;
65
+ return {
66
+ onStart: (props) => {
67
+ if (typeof document === 'undefined')
68
+ return;
69
+ const popupEl = createPopup();
70
+ const listEl = document.createElement('div');
71
+ listEl.setAttribute('role', 'listbox');
72
+ popupEl.appendChild(listEl);
73
+ document.body.appendChild(popupEl);
74
+ state = {
75
+ items: props.items,
76
+ selectedIndex: 0,
77
+ popupEl,
78
+ listEl,
79
+ cleanup: null,
80
+ pickItem: (i) => {
81
+ const it = state?.items[i];
82
+ if (!it)
83
+ return;
84
+ props.command({ ...it });
85
+ }
86
+ };
87
+ renderItems(state);
88
+ const rect = props.clientRect?.();
89
+ if (rect) {
90
+ const virtualEl = {
91
+ getBoundingClientRect: () => rect
92
+ };
93
+ state.cleanup = autoUpdate(virtualEl, popupEl, () => {
94
+ void computePosition(virtualEl, popupEl, {
95
+ placement: 'bottom-start',
96
+ middleware: [offset(6), flip(), shift({ padding: 8 })]
97
+ }).then(({ x, y }) => {
98
+ popupEl.style.left = `${x}px`;
99
+ popupEl.style.top = `${y}px`;
100
+ });
101
+ });
102
+ }
103
+ },
104
+ onUpdate: (props) => {
105
+ if (!state)
106
+ return;
107
+ state.items = props.items;
108
+ state.selectedIndex = 0;
109
+ renderItems(state);
110
+ },
111
+ onKeyDown: (props) => {
112
+ if (!state)
113
+ return false;
114
+ const { event } = props;
115
+ if (event.key === 'ArrowDown') {
116
+ state.selectedIndex =
117
+ (state.selectedIndex + 1) % Math.max(state.items.length, 1);
118
+ renderItems(state);
119
+ return true;
120
+ }
121
+ if (event.key === 'ArrowUp') {
122
+ state.selectedIndex =
123
+ (state.selectedIndex + state.items.length - 1) %
124
+ Math.max(state.items.length, 1);
125
+ renderItems(state);
126
+ return true;
127
+ }
128
+ if (event.key === 'Enter') {
129
+ state.pickItem(state.selectedIndex);
130
+ return true;
131
+ }
132
+ return false;
133
+ },
134
+ onExit: () => {
135
+ state?.cleanup?.();
136
+ state?.popupEl?.remove();
137
+ state = null;
138
+ }
139
+ };
140
+ }
141
+ };
142
+ }
@@ -0,0 +1,11 @@
1
+ import type { Editor } from '@tiptap/core';
2
+ import type { EditorReactiveState, ToolbarAction } from './editor.types.js';
3
+ export interface ToolbarActionDef {
4
+ icon: string;
5
+ label: string;
6
+ isActive?: (state: EditorReactiveState) => boolean | undefined;
7
+ isDisabled?: (state: EditorReactiveState) => boolean | undefined;
8
+ run: (editor: Editor) => void;
9
+ }
10
+ export declare const TOOLBAR_ACTIONS: Record<ToolbarAction, ToolbarActionDef>;
11
+ export declare const DEFAULT_TOOLBAR: (ToolbarAction | '|')[];
@@ -0,0 +1,212 @@
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
+ function promptForLink(editor) {
10
+ const previous = editor.getAttributes('link').href ?? 'https://';
11
+ const url = typeof window === 'undefined' ? null : window.prompt('URL', previous);
12
+ if (url === null)
13
+ return;
14
+ if (url.trim() === '') {
15
+ editor.chain().focus().extendMarkRange('link').unsetLink().run();
16
+ return;
17
+ }
18
+ editor.chain().focus().extendMarkRange('link').setLink({ href: url }).run();
19
+ }
20
+ export const TOOLBAR_ACTIONS = {
21
+ bold: {
22
+ icon: 'lucide:bold',
23
+ label: 'Bold',
24
+ isActive: (s) => s.active.bold,
25
+ run: (ed) => ed.chain().focus().toggleBold().run()
26
+ },
27
+ italic: {
28
+ icon: 'lucide:italic',
29
+ label: 'Italic',
30
+ isActive: (s) => s.active.italic,
31
+ run: (ed) => ed.chain().focus().toggleItalic().run()
32
+ },
33
+ underline: {
34
+ icon: 'lucide:underline',
35
+ label: 'Underline',
36
+ isActive: (s) => s.active.underline,
37
+ run: (ed) => ed.chain().focus().toggleUnderline().run()
38
+ },
39
+ strike: {
40
+ icon: 'lucide:strikethrough',
41
+ label: 'Strikethrough',
42
+ isActive: (s) => s.active.strike,
43
+ run: (ed) => ed.chain().focus().toggleStrike().run()
44
+ },
45
+ code: {
46
+ icon: 'lucide:code',
47
+ label: 'Inline code',
48
+ isActive: (s) => s.active.code,
49
+ run: (ed) => ed.chain().focus().toggleCode().run()
50
+ },
51
+ h1: {
52
+ icon: 'lucide:heading-1',
53
+ label: 'Heading 1',
54
+ isActive: (s) => s.active.h1,
55
+ run: (ed) => ed.chain().focus().toggleHeading({ level: 1 }).run()
56
+ },
57
+ h2: {
58
+ icon: 'lucide:heading-2',
59
+ label: 'Heading 2',
60
+ isActive: (s) => s.active.h2,
61
+ run: (ed) => ed.chain().focus().toggleHeading({ level: 2 }).run()
62
+ },
63
+ h3: {
64
+ icon: 'lucide:heading-3',
65
+ label: 'Heading 3',
66
+ isActive: (s) => s.active.h3,
67
+ run: (ed) => ed.chain().focus().toggleHeading({ level: 3 }).run()
68
+ },
69
+ paragraph: {
70
+ icon: 'lucide:pilcrow',
71
+ label: 'Paragraph',
72
+ isActive: (s) => s.active.paragraph,
73
+ run: (ed) => ed.chain().focus().setParagraph().run()
74
+ },
75
+ bulletList: {
76
+ icon: 'lucide:list',
77
+ label: 'Bullet list',
78
+ isActive: (s) => s.active.bulletList,
79
+ run: (ed) => ed.chain().focus().toggleBulletList().run()
80
+ },
81
+ orderedList: {
82
+ icon: 'lucide:list-ordered',
83
+ label: 'Numbered list',
84
+ isActive: (s) => s.active.orderedList,
85
+ run: (ed) => ed.chain().focus().toggleOrderedList().run()
86
+ },
87
+ blockquote: {
88
+ icon: 'lucide:quote',
89
+ label: 'Quote',
90
+ isActive: (s) => s.active.blockquote,
91
+ run: (ed) => ed.chain().focus().toggleBlockquote().run()
92
+ },
93
+ codeBlock: {
94
+ icon: 'lucide:square-code',
95
+ label: 'Code block',
96
+ isActive: (s) => s.active.codeBlock,
97
+ run: (ed) => ed.chain().focus().toggleCodeBlock().run()
98
+ },
99
+ horizontalRule: {
100
+ icon: 'lucide:minus',
101
+ label: 'Horizontal rule',
102
+ run: (ed) => ed.chain().focus().setHorizontalRule().run()
103
+ },
104
+ link: {
105
+ icon: 'lucide:link',
106
+ label: 'Insert link',
107
+ isActive: (s) => s.active.link,
108
+ run: promptForLink
109
+ },
110
+ unlink: {
111
+ icon: 'lucide:unlink',
112
+ label: 'Remove link',
113
+ isDisabled: (s) => !s.active.link,
114
+ run: (ed) => ed.chain().focus().unsetLink().run()
115
+ },
116
+ alignLeft: {
117
+ icon: 'lucide:align-left',
118
+ label: 'Align left',
119
+ isActive: (s) => s.active.alignLeft,
120
+ run: (ed) => ed.chain().focus().setTextAlign('left').run()
121
+ },
122
+ alignCenter: {
123
+ icon: 'lucide:align-center',
124
+ label: 'Align center',
125
+ isActive: (s) => s.active.alignCenter,
126
+ run: (ed) => ed.chain().focus().setTextAlign('center').run()
127
+ },
128
+ alignRight: {
129
+ icon: 'lucide:align-right',
130
+ label: 'Align right',
131
+ isActive: (s) => s.active.alignRight,
132
+ run: (ed) => ed.chain().focus().setTextAlign('right').run()
133
+ },
134
+ alignJustify: {
135
+ icon: 'lucide:align-justify',
136
+ label: 'Align justify',
137
+ isActive: (s) => s.active.alignJustify,
138
+ run: (ed) => ed.chain().focus().setTextAlign('justify').run()
139
+ },
140
+ undo: {
141
+ icon: 'lucide:undo-2',
142
+ label: 'Undo',
143
+ isDisabled: (s) => !s.can.undo,
144
+ run: (ed) => ed.chain().focus().undo().run()
145
+ },
146
+ redo: {
147
+ icon: 'lucide:redo-2',
148
+ label: 'Redo',
149
+ isDisabled: (s) => !s.can.redo,
150
+ run: (ed) => ed.chain().focus().redo().run()
151
+ },
152
+ clearFormatting: {
153
+ icon: 'lucide:remove-formatting',
154
+ label: 'Clear formatting',
155
+ run: (ed) => ed.chain().focus().unsetAllMarks().clearNodes().run()
156
+ },
157
+ image: {
158
+ icon: 'lucide:image',
159
+ label: 'Insert image',
160
+ run: (ed) => {
161
+ if (typeof window === 'undefined')
162
+ return;
163
+ const url = window.prompt('Image URL', 'https://');
164
+ if (!url)
165
+ return;
166
+ ed.chain().focus().setImage({ src: url }).run();
167
+ }
168
+ },
169
+ table: {
170
+ icon: 'lucide:table',
171
+ label: 'Insert table',
172
+ run: (ed) => {
173
+ ed.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run();
174
+ }
175
+ },
176
+ youtube: {
177
+ icon: 'lucide:youtube',
178
+ label: 'Insert YouTube video',
179
+ run: (ed) => {
180
+ if (typeof window === 'undefined')
181
+ return;
182
+ const url = window.prompt('YouTube URL', 'https://youtu.be/');
183
+ if (!url)
184
+ return;
185
+ ed.commands.setYoutubeVideo({ src: url });
186
+ }
187
+ }
188
+ };
189
+ export const DEFAULT_TOOLBAR = [
190
+ 'bold',
191
+ 'italic',
192
+ 'underline',
193
+ 'strike',
194
+ '|',
195
+ 'h1',
196
+ 'h2',
197
+ 'h3',
198
+ '|',
199
+ 'bulletList',
200
+ 'orderedList',
201
+ 'blockquote',
202
+ 'codeBlock',
203
+ '|',
204
+ 'link',
205
+ '|',
206
+ 'alignLeft',
207
+ 'alignCenter',
208
+ 'alignRight',
209
+ '|',
210
+ 'undo',
211
+ 'redo'
212
+ ];