luxen-ui 0.6.2 → 0.7.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/cdn/chunks/decorate.js +1 -1
- package/cdn/chunks/floating-ui.dom.js +2 -0
- package/cdn/chunks/floating-ui.dom.js.map +1 -0
- package/cdn/chunks/lit.js +1 -1
- package/cdn/chunks/module.js +717 -0
- package/cdn/chunks/module.js.map +1 -0
- package/cdn/chunks/native.js +2 -0
- package/cdn/chunks/native.js.map +1 -0
- package/cdn/custom-elements.json +10269 -7939
- package/cdn/elements/avatar/avatar.js +7 -7
- package/cdn/elements/avatar/avatar.js.map +1 -1
- package/cdn/elements/carousel/carousel.js +5 -5
- package/cdn/elements/carousel-item/carousel-item.js +1 -1
- package/cdn/elements/dialog/dialog.js +1 -1
- package/cdn/elements/dialog/dialog.styles.js +1 -1
- package/cdn/elements/drawer/drawer.js +1 -1
- package/cdn/elements/dropdown/dropdown.js +1 -1
- package/cdn/elements/dropdown-item/dropdown-item.js +5 -5
- package/cdn/elements/icon/icon.js +1 -1
- package/cdn/elements/popover/popover.js +2 -2
- package/cdn/elements/prose-editor/index.d.ts +2 -0
- package/cdn/elements/prose-editor/index.d.ts.map +1 -0
- package/cdn/elements/prose-editor/index.js +2 -0
- package/cdn/elements/prose-editor/index.js.map +1 -0
- package/cdn/elements/prose-editor/prose-editor.d.ts +110 -0
- package/cdn/elements/prose-editor/prose-editor.d.ts.map +1 -0
- package/cdn/elements/prose-editor/prose-editor.js +180 -0
- package/cdn/elements/prose-editor/prose-editor.js.map +1 -0
- package/cdn/elements/rating/rating.js +5 -5
- package/cdn/elements/spinner/spinner.js +1 -1
- package/cdn/elements/sticky-bar/sticky-bar.js +1 -1
- package/cdn/elements/stories-viewer/stories-viewer.js +7 -7
- package/cdn/elements/story/story.js +1 -1
- package/cdn/elements/tooltip/tooltip.js +2 -2
- package/cdn/elements/tree/tree.js +1 -1
- package/cdn/elements/tree-item/tree-item.js +4 -4
- package/cdn/registry.d.ts +1 -1
- package/cdn/registry.d.ts.map +1 -1
- package/cdn/registry.js.map +1 -1
- package/cdn/shared/controllers/popover.js +1 -1
- package/cdn/shared/controllers/popover.js.map +1 -1
- package/cdn/shared/styles/host.styles.js +1 -1
- package/cdn/standalone.css +132 -1
- package/cdn/standalone.js +25567 -149
- package/cdn/standalone.js.map +1 -1
- package/cdn/styles/elements/prose-editor.css +129 -0
- package/cdn/styles/elements/toast.css +1 -1
- package/dist/css/elements/prose-editor.css +129 -0
- package/dist/css/elements/toast.css +1 -1
- package/dist/custom-elements.json +10269 -7939
- package/dist/elements/prose-editor/index.d.ts +2 -0
- package/dist/elements/prose-editor/index.d.ts.map +1 -0
- package/dist/elements/prose-editor/index.js +4 -0
- package/dist/elements/prose-editor/prose-editor.css +133 -0
- package/dist/elements/prose-editor/prose-editor.d.ts +111 -0
- package/dist/elements/prose-editor/prose-editor.d.ts.map +1 -0
- package/dist/elements/prose-editor/prose-editor.js +475 -0
- package/dist/registry.d.ts +1 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/templates/elements/prose-editor.md +216 -0
- package/elements.json +7 -0
- package/package.json +8 -1
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
+
};
|
|
12
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
13
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
14
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
15
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
16
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
17
|
+
};
|
|
18
|
+
var _ProseEditor_initialHtml_accessor_storage, _ProseEditor_initialJson_accessor_storage, _ProseEditor_editorClass_accessor_storage, _ProseEditor_toolbar_accessor_storage, _ProseEditor_toolbarPreset_accessor_storage, _ProseEditor_toolbarPlacement_accessor_storage, _ProseEditor_autofocus_accessor_storage, _ProseEditor_placeholder_accessor_storage, _ProseEditor__emojiPickerActive_accessor_storage;
|
|
19
|
+
import { html, unsafeCSS } from 'lit';
|
|
20
|
+
import { property, state } from 'lit/decorators.js';
|
|
21
|
+
import { classMap } from 'lit/directives/class-map.js';
|
|
22
|
+
import { map } from 'lit/directives/map.js';
|
|
23
|
+
import { computePosition, flip, offset, shift } from '@floating-ui/dom';
|
|
24
|
+
import { Editor } from '@tiptap/core';
|
|
25
|
+
import StarterKit from '@tiptap/starter-kit';
|
|
26
|
+
import Highlight from '@tiptap/extension-highlight';
|
|
27
|
+
import { Placeholder } from '@tiptap/extensions';
|
|
28
|
+
import { LuxenFormAssociatedElement } from '../../shared/luxen-form-associated-element.js';
|
|
29
|
+
import hostStyles from '../../shared/styles/host.styles.js';
|
|
30
|
+
import '../icon/index.js';
|
|
31
|
+
import rawStyles from './prose-editor.css?inline';
|
|
32
|
+
const styles = unsafeCSS(rawStyles);
|
|
33
|
+
const TOOLBAR_PRESETS = {
|
|
34
|
+
minimal: ['bold', 'italic', 'underline'],
|
|
35
|
+
default: [
|
|
36
|
+
'heading-1',
|
|
37
|
+
'heading-2',
|
|
38
|
+
'heading-3',
|
|
39
|
+
'divider',
|
|
40
|
+
'bold',
|
|
41
|
+
'italic',
|
|
42
|
+
'underline',
|
|
43
|
+
'highlight',
|
|
44
|
+
'bulletlist',
|
|
45
|
+
'orderedlist',
|
|
46
|
+
'blockquote',
|
|
47
|
+
'divider',
|
|
48
|
+
'code-block',
|
|
49
|
+
'horizontal-rule',
|
|
50
|
+
'link',
|
|
51
|
+
'emoji',
|
|
52
|
+
'divider',
|
|
53
|
+
'undo',
|
|
54
|
+
'redo',
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* @summary A rich text editor built on Tiptap (ProseMirror). Form-associated: its value is the editor HTML.
|
|
59
|
+
* @customElement l-prose-editor
|
|
60
|
+
*
|
|
61
|
+
* @slot toolbar-start - Content placed before the generated toolbar buttons.
|
|
62
|
+
* @slot toolbar-end - Content placed after the generated toolbar buttons.
|
|
63
|
+
*
|
|
64
|
+
* @event change - Fired when the content changes. `detail` is `{ html, json }`.
|
|
65
|
+
* @event add-file - Fired when the attachment toolbar button is clicked.
|
|
66
|
+
*
|
|
67
|
+
* @csspart wrapper - The editor frame wrapping the toolbar and content.
|
|
68
|
+
* @csspart toolbar - The toolbar row.
|
|
69
|
+
* @csspart toolbar-button - Any toolbar button.
|
|
70
|
+
* @csspart divider - A toolbar divider.
|
|
71
|
+
* @csspart editor - The container around the editable content.
|
|
72
|
+
*
|
|
73
|
+
* @cssproperty --border-color - Color of the editor frame border.
|
|
74
|
+
* @cssproperty --border-width - Width of the editor frame border.
|
|
75
|
+
* @cssproperty --border-radius - Corner radius of the editor frame.
|
|
76
|
+
* @cssproperty --background - Background color of the editor.
|
|
77
|
+
* @cssproperty --color - Text color of the editor.
|
|
78
|
+
* @cssproperty --toolbar-background - Background color of the toolbar.
|
|
79
|
+
* @cssproperty --toolbar-padding - Padding around the toolbar.
|
|
80
|
+
* @cssproperty --toolbar-gap - Gap between toolbar buttons.
|
|
81
|
+
* @cssproperty --toolbar-divider-color - Color of toolbar dividers.
|
|
82
|
+
* @cssproperty --toolbar-button-size - Size of toolbar buttons.
|
|
83
|
+
* @cssproperty --toolbar-button-radius - Corner radius of toolbar buttons.
|
|
84
|
+
* @cssproperty --toolbar-button-color - Icon color of inactive toolbar buttons.
|
|
85
|
+
* @cssproperty --toolbar-button-color-active - Icon color of hovered/active toolbar buttons.
|
|
86
|
+
* @cssproperty --toolbar-button-background-hover - Background of hovered toolbar buttons.
|
|
87
|
+
* @cssproperty --toolbar-button-background-active - Background of active toolbar buttons.
|
|
88
|
+
*/
|
|
89
|
+
export class ProseEditor extends LuxenFormAssociatedElement {
|
|
90
|
+
constructor() {
|
|
91
|
+
super(...arguments);
|
|
92
|
+
_ProseEditor_initialHtml_accessor_storage.set(this, '');
|
|
93
|
+
_ProseEditor_initialJson_accessor_storage.set(this, '');
|
|
94
|
+
_ProseEditor_editorClass_accessor_storage.set(this, 'prose');
|
|
95
|
+
_ProseEditor_toolbar_accessor_storage.set(this, []);
|
|
96
|
+
_ProseEditor_toolbarPreset_accessor_storage.set(this, 'default');
|
|
97
|
+
_ProseEditor_toolbarPlacement_accessor_storage.set(this, 'top');
|
|
98
|
+
_ProseEditor_autofocus_accessor_storage.set(this, false);
|
|
99
|
+
_ProseEditor_placeholder_accessor_storage.set(this, '');
|
|
100
|
+
_ProseEditor__emojiPickerActive_accessor_storage.set(this, false);
|
|
101
|
+
this._onFocus = () => {
|
|
102
|
+
if (!this.disabled && document.activeElement !== this.editor?.view.dom) {
|
|
103
|
+
this.focus();
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
this._onKeyDown = (event) => {
|
|
107
|
+
if (event.key === 'Escape')
|
|
108
|
+
this._emojiPickerActive = false;
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/** Initial HTML content. */
|
|
112
|
+
get initialHtml() { return __classPrivateFieldGet(this, _ProseEditor_initialHtml_accessor_storage, "f"); }
|
|
113
|
+
set initialHtml(value) { __classPrivateFieldSet(this, _ProseEditor_initialHtml_accessor_storage, value, "f"); }
|
|
114
|
+
/** Initial content as a serialized ProseMirror JSON string. */
|
|
115
|
+
get initialJson() { return __classPrivateFieldGet(this, _ProseEditor_initialJson_accessor_storage, "f"); }
|
|
116
|
+
set initialJson(value) { __classPrivateFieldSet(this, _ProseEditor_initialJson_accessor_storage, value, "f"); }
|
|
117
|
+
/** Class applied to the `.ProseMirror` editable element (e.g. for Tailwind Typography `prose`). */
|
|
118
|
+
get editorClass() { return __classPrivateFieldGet(this, _ProseEditor_editorClass_accessor_storage, "f"); }
|
|
119
|
+
set editorClass(value) { __classPrivateFieldSet(this, _ProseEditor_editorClass_accessor_storage, value, "f"); }
|
|
120
|
+
/** Explicit list of toolbar commands. Overrides `toolbar-preset` when set. */
|
|
121
|
+
get toolbar() { return __classPrivateFieldGet(this, _ProseEditor_toolbar_accessor_storage, "f"); }
|
|
122
|
+
set toolbar(value) { __classPrivateFieldSet(this, _ProseEditor_toolbar_accessor_storage, value, "f"); }
|
|
123
|
+
/** Built-in toolbar layout used when `toolbar` is not set. */
|
|
124
|
+
get toolbarPreset() { return __classPrivateFieldGet(this, _ProseEditor_toolbarPreset_accessor_storage, "f"); }
|
|
125
|
+
set toolbarPreset(value) { __classPrivateFieldSet(this, _ProseEditor_toolbarPreset_accessor_storage, value, "f"); }
|
|
126
|
+
/** Where the toolbar sits relative to the content. */
|
|
127
|
+
get toolbarPlacement() { return __classPrivateFieldGet(this, _ProseEditor_toolbarPlacement_accessor_storage, "f"); }
|
|
128
|
+
set toolbarPlacement(value) { __classPrivateFieldSet(this, _ProseEditor_toolbarPlacement_accessor_storage, value, "f"); }
|
|
129
|
+
/** Focus the editor on creation. */
|
|
130
|
+
get autofocus() { return __classPrivateFieldGet(this, _ProseEditor_autofocus_accessor_storage, "f"); }
|
|
131
|
+
set autofocus(value) { __classPrivateFieldSet(this, _ProseEditor_autofocus_accessor_storage, value, "f"); }
|
|
132
|
+
/** Placeholder shown when the editor is empty. */
|
|
133
|
+
get placeholder() { return __classPrivateFieldGet(this, _ProseEditor_placeholder_accessor_storage, "f"); }
|
|
134
|
+
set placeholder(value) { __classPrivateFieldSet(this, _ProseEditor_placeholder_accessor_storage, value, "f"); }
|
|
135
|
+
get _emojiPickerActive() { return __classPrivateFieldGet(this, _ProseEditor__emojiPickerActive_accessor_storage, "f"); }
|
|
136
|
+
set _emojiPickerActive(value) { __classPrivateFieldSet(this, _ProseEditor__emojiPickerActive_accessor_storage, value, "f"); }
|
|
137
|
+
get validationTarget() {
|
|
138
|
+
return this._editorRoot;
|
|
139
|
+
}
|
|
140
|
+
get _toolbar() {
|
|
141
|
+
return this.toolbar.length > 0 ? this.toolbar : TOOLBAR_PRESETS[this.toolbarPreset];
|
|
142
|
+
}
|
|
143
|
+
firstUpdated() {
|
|
144
|
+
this.editor = new Editor({
|
|
145
|
+
element: this._createEditorRoot(),
|
|
146
|
+
editorProps: {
|
|
147
|
+
attributes: { class: this.editorClass },
|
|
148
|
+
},
|
|
149
|
+
extensions: [
|
|
150
|
+
StarterKit.configure({ link: { openOnClick: false } }),
|
|
151
|
+
Highlight,
|
|
152
|
+
Placeholder.configure({ placeholder: this.placeholder }),
|
|
153
|
+
],
|
|
154
|
+
content: this._initialContent(),
|
|
155
|
+
editable: !this.disabled,
|
|
156
|
+
autofocus: this.autofocus && !this.disabled,
|
|
157
|
+
onUpdate: () => {
|
|
158
|
+
this.requestUpdate();
|
|
159
|
+
this._emitChange();
|
|
160
|
+
},
|
|
161
|
+
onTransaction: () => this.requestUpdate(),
|
|
162
|
+
onSelectionUpdate: () => this.requestUpdate(),
|
|
163
|
+
});
|
|
164
|
+
document.addEventListener('keydown', this._onKeyDown);
|
|
165
|
+
this.addEventListener('focus', this._onFocus);
|
|
166
|
+
this._syncValue();
|
|
167
|
+
this.requestUpdate();
|
|
168
|
+
}
|
|
169
|
+
updated(changed) {
|
|
170
|
+
if (changed.has('disabled') && this.editor) {
|
|
171
|
+
this.editor.setEditable(!this.disabled);
|
|
172
|
+
}
|
|
173
|
+
if (changed.has('_emojiPickerActive')) {
|
|
174
|
+
void this._positionEmojiPicker();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
disconnectedCallback() {
|
|
178
|
+
super.disconnectedCallback();
|
|
179
|
+
document.removeEventListener('keydown', this._onKeyDown);
|
|
180
|
+
this.removeEventListener('focus', this._onFocus);
|
|
181
|
+
this.editor?.destroy();
|
|
182
|
+
this._editorRoot?.remove();
|
|
183
|
+
this._emojiPicker?.remove();
|
|
184
|
+
}
|
|
185
|
+
// --- Public API ---
|
|
186
|
+
/** Get the current content as an HTML string. Empty paragraph resolves to `''`. */
|
|
187
|
+
getHTML() {
|
|
188
|
+
if (!this.editor)
|
|
189
|
+
return '';
|
|
190
|
+
const value = this.editor.getHTML();
|
|
191
|
+
return value === '<p></p>' ? '' : value;
|
|
192
|
+
}
|
|
193
|
+
/** Get the current content as ProseMirror JSON. */
|
|
194
|
+
getJSON() {
|
|
195
|
+
return this.editor?.getJSON() ?? {};
|
|
196
|
+
}
|
|
197
|
+
/** Remove all content. */
|
|
198
|
+
clear() {
|
|
199
|
+
this.editor.commands.clearContent(true);
|
|
200
|
+
}
|
|
201
|
+
focus() {
|
|
202
|
+
this.editor.commands.focus();
|
|
203
|
+
}
|
|
204
|
+
blur() {
|
|
205
|
+
this.editor.commands.blur();
|
|
206
|
+
}
|
|
207
|
+
toggleBold() {
|
|
208
|
+
this.editor.chain().focus().toggleBold().run();
|
|
209
|
+
}
|
|
210
|
+
toggleItalic() {
|
|
211
|
+
this.editor.chain().focus().toggleItalic().run();
|
|
212
|
+
}
|
|
213
|
+
toggleUnderline() {
|
|
214
|
+
this.editor.chain().focus().toggleUnderline().run();
|
|
215
|
+
}
|
|
216
|
+
toggleStrike() {
|
|
217
|
+
this.editor.chain().focus().toggleStrike().run();
|
|
218
|
+
}
|
|
219
|
+
toggleHighlight() {
|
|
220
|
+
this.editor.chain().focus().toggleHighlight().run();
|
|
221
|
+
}
|
|
222
|
+
toggleHeading(level) {
|
|
223
|
+
this.editor.chain().focus().toggleHeading({ level }).run();
|
|
224
|
+
}
|
|
225
|
+
toggleBulletList() {
|
|
226
|
+
this.editor.chain().focus().toggleBulletList().run();
|
|
227
|
+
}
|
|
228
|
+
toggleOrderedList() {
|
|
229
|
+
this.editor.chain().focus().toggleOrderedList().run();
|
|
230
|
+
}
|
|
231
|
+
toggleBlockquote() {
|
|
232
|
+
this.editor.chain().focus().toggleBlockquote().run();
|
|
233
|
+
}
|
|
234
|
+
toggleCodeBlock() {
|
|
235
|
+
this.editor.chain().focus().toggleCodeBlock().run();
|
|
236
|
+
}
|
|
237
|
+
setHorizontalRule() {
|
|
238
|
+
this.editor.chain().focus().setHorizontalRule().run();
|
|
239
|
+
}
|
|
240
|
+
undo() {
|
|
241
|
+
this.editor.chain().focus().undo().run();
|
|
242
|
+
}
|
|
243
|
+
redo() {
|
|
244
|
+
this.editor.chain().focus().redo().run();
|
|
245
|
+
}
|
|
246
|
+
toggleLink() {
|
|
247
|
+
if (this.editor.isActive('link')) {
|
|
248
|
+
this.editor.chain().focus().unsetLink().run();
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
const input = window.prompt('URL');
|
|
252
|
+
if (!input)
|
|
253
|
+
return;
|
|
254
|
+
const url = /^https?:\/\//.test(input) ? input : `https://${input}`;
|
|
255
|
+
this.editor.chain().focus().setLink({ href: url }).run();
|
|
256
|
+
}
|
|
257
|
+
// --- Form association ---
|
|
258
|
+
formResetCallback() {
|
|
259
|
+
super.formResetCallback();
|
|
260
|
+
this.editor?.commands.setContent(this._initialContent());
|
|
261
|
+
}
|
|
262
|
+
/** Sync the form value and validity from the current content. Returns the HTML. */
|
|
263
|
+
_syncValue() {
|
|
264
|
+
if (!this.editor)
|
|
265
|
+
return undefined;
|
|
266
|
+
const value = this.getHTML();
|
|
267
|
+
this._syncFormValue(value);
|
|
268
|
+
if (this.required) {
|
|
269
|
+
this.setValidity(value ? {} : { valueMissing: true }, value ? '' : 'Please fill out this field.');
|
|
270
|
+
}
|
|
271
|
+
return value;
|
|
272
|
+
}
|
|
273
|
+
_emitChange() {
|
|
274
|
+
const value = this._syncValue();
|
|
275
|
+
if (value === undefined)
|
|
276
|
+
return;
|
|
277
|
+
this.emit('change', { detail: { html: value, json: this.getJSON() } });
|
|
278
|
+
}
|
|
279
|
+
// --- Editor wiring ---
|
|
280
|
+
_initialContent() {
|
|
281
|
+
if (this.initialHtml)
|
|
282
|
+
return this.initialHtml;
|
|
283
|
+
if (this.initialJson) {
|
|
284
|
+
return JSON.parse(this.initialJson);
|
|
285
|
+
}
|
|
286
|
+
return '';
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* The editable element is created in light DOM (slotted), not the shadow root.
|
|
290
|
+
* Firefox and WebKit have long-standing bugs with `contenteditable` carets and
|
|
291
|
+
* DOM selections inside a shadow tree, so ProseMirror must live outside it.
|
|
292
|
+
* @see https://bugzilla.mozilla.org/show_bug.cgi?id=1496769
|
|
293
|
+
* @see https://bugs.webkit.org/show_bug.cgi?id=163921
|
|
294
|
+
*/
|
|
295
|
+
_createEditorRoot() {
|
|
296
|
+
const root = document.createElement('div');
|
|
297
|
+
root.slot = 'editor';
|
|
298
|
+
this.append(root);
|
|
299
|
+
this._editorRoot = root;
|
|
300
|
+
return root;
|
|
301
|
+
}
|
|
302
|
+
// --- Emoji picker (lazy-loaded) ---
|
|
303
|
+
async _toggleEmojiPicker() {
|
|
304
|
+
if (!this._emojiPicker) {
|
|
305
|
+
const [{ Picker }, { default: data }] = await Promise.all([
|
|
306
|
+
import('emoji-mart'),
|
|
307
|
+
import('@emoji-mart/data'),
|
|
308
|
+
]);
|
|
309
|
+
const dark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
310
|
+
this._emojiPicker = new Picker({
|
|
311
|
+
parent: document.body,
|
|
312
|
+
data,
|
|
313
|
+
theme: dark ? 'dark' : 'light',
|
|
314
|
+
onEmojiSelect: ({ native }) => {
|
|
315
|
+
this.editor.chain().focus().insertContent(native).run();
|
|
316
|
+
this._emojiPickerActive = false;
|
|
317
|
+
},
|
|
318
|
+
onClickOutside: (event) => {
|
|
319
|
+
if (!event.composedPath().includes(this._emojiButton)) {
|
|
320
|
+
this._emojiPickerActive = false;
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
});
|
|
324
|
+
Object.assign(this._emojiPicker.style, { position: 'absolute', zIndex: '999' });
|
|
325
|
+
}
|
|
326
|
+
this._emojiPickerActive = !this._emojiPickerActive;
|
|
327
|
+
}
|
|
328
|
+
get _emojiButton() {
|
|
329
|
+
return this.shadowRoot?.querySelector('[data-command="emoji"]') ?? null;
|
|
330
|
+
}
|
|
331
|
+
async _positionEmojiPicker() {
|
|
332
|
+
const button = this._emojiButton;
|
|
333
|
+
if (!button || !this._emojiPicker)
|
|
334
|
+
return;
|
|
335
|
+
if (!this._emojiPickerActive) {
|
|
336
|
+
Object.assign(this._emojiPicker.style, { visibility: 'hidden', pointerEvents: 'none' });
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
const { x, y, strategy } = await computePosition(button, this._emojiPicker, {
|
|
340
|
+
placement: 'bottom',
|
|
341
|
+
middleware: [offset(4), flip(), shift({ padding: 8 })],
|
|
342
|
+
});
|
|
343
|
+
Object.assign(this._emojiPicker.style, {
|
|
344
|
+
position: strategy,
|
|
345
|
+
left: `${x}px`,
|
|
346
|
+
top: `${y}px`,
|
|
347
|
+
visibility: 'visible',
|
|
348
|
+
pointerEvents: 'auto',
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
// --- Rendering ---
|
|
352
|
+
_renderButton(command, label, icon, onClick, active = false) {
|
|
353
|
+
return html `
|
|
354
|
+
<button
|
|
355
|
+
type="button"
|
|
356
|
+
part="toolbar-button"
|
|
357
|
+
class="toolbar-button ${classMap({ 'is-active': active })}"
|
|
358
|
+
data-command=${command}
|
|
359
|
+
aria-label=${label}
|
|
360
|
+
aria-pressed=${active}
|
|
361
|
+
title=${label}
|
|
362
|
+
@click=${onClick}
|
|
363
|
+
>
|
|
364
|
+
<l-icon name=${icon}></l-icon>
|
|
365
|
+
</button>
|
|
366
|
+
`;
|
|
367
|
+
}
|
|
368
|
+
_renderToolbarItem(command) {
|
|
369
|
+
const editor = this.editor;
|
|
370
|
+
switch (command) {
|
|
371
|
+
case 'divider':
|
|
372
|
+
return html `<span
|
|
373
|
+
part="divider"
|
|
374
|
+
class="divider"
|
|
375
|
+
></span>`;
|
|
376
|
+
case 'heading-1':
|
|
377
|
+
return this._renderButton('heading-1', 'Heading 1', 'ri:h-1', () => this.toggleHeading(1), editor.isActive('heading', { level: 1 }));
|
|
378
|
+
case 'heading-2':
|
|
379
|
+
return this._renderButton('heading-2', 'Heading 2', 'ri:h-2', () => this.toggleHeading(2), editor.isActive('heading', { level: 2 }));
|
|
380
|
+
case 'heading-3':
|
|
381
|
+
return this._renderButton('heading-3', 'Heading 3', 'ri:h-3', () => this.toggleHeading(3), editor.isActive('heading', { level: 3 }));
|
|
382
|
+
case 'bold':
|
|
383
|
+
return this._renderButton('bold', 'Bold', 'ri:bold', () => this.toggleBold(), editor.isActive('bold'));
|
|
384
|
+
case 'italic':
|
|
385
|
+
return this._renderButton('italic', 'Italic', 'ri:italic', () => this.toggleItalic(), editor.isActive('italic'));
|
|
386
|
+
case 'underline':
|
|
387
|
+
return this._renderButton('underline', 'Underline', 'ri:underline', () => this.toggleUnderline(), editor.isActive('underline'));
|
|
388
|
+
case 'strike':
|
|
389
|
+
return this._renderButton('strike', 'Strikethrough', 'ri:strikethrough', () => this.toggleStrike(), editor.isActive('strike'));
|
|
390
|
+
case 'highlight':
|
|
391
|
+
return this._renderButton('highlight', 'Highlight', 'ri:mark-pen-line', () => this.toggleHighlight(), editor.isActive('highlight'));
|
|
392
|
+
case 'bulletlist':
|
|
393
|
+
return this._renderButton('bulletlist', 'Bullet list', 'ri:list-unordered', () => this.toggleBulletList(), editor.isActive('bulletList'));
|
|
394
|
+
case 'orderedlist':
|
|
395
|
+
return this._renderButton('orderedlist', 'Ordered list', 'ri:list-ordered', () => this.toggleOrderedList(), editor.isActive('orderedList'));
|
|
396
|
+
case 'blockquote':
|
|
397
|
+
return this._renderButton('blockquote', 'Blockquote', 'ri:double-quotes-l', () => this.toggleBlockquote(), editor.isActive('blockquote'));
|
|
398
|
+
case 'code-block':
|
|
399
|
+
return this._renderButton('code-block', 'Code block', 'ri:code-box-line', () => this.toggleCodeBlock(), editor.isActive('codeBlock'));
|
|
400
|
+
case 'horizontal-rule':
|
|
401
|
+
return this._renderButton('horizontal-rule', 'Horizontal rule', 'ri:separator', () => this.setHorizontalRule());
|
|
402
|
+
case 'link':
|
|
403
|
+
return this._renderButton('link', 'Link', 'ri:link', () => this.toggleLink(), editor.isActive('link'));
|
|
404
|
+
case 'emoji':
|
|
405
|
+
return this._renderButton('emoji', 'Emoji', 'ri:emotion-line', () => void this._toggleEmojiPicker());
|
|
406
|
+
case 'attachment':
|
|
407
|
+
return this._renderButton('attachment', 'Attach file', 'ri:attachment-2', () => this.emit('add-file'));
|
|
408
|
+
case 'undo':
|
|
409
|
+
return this._renderButton('undo', 'Undo', 'ri:arrow-go-back-line', () => this.undo());
|
|
410
|
+
case 'redo':
|
|
411
|
+
return this._renderButton('redo', 'Redo', 'ri:arrow-go-forward-line', () => this.redo());
|
|
412
|
+
default:
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
render() {
|
|
417
|
+
return html `
|
|
418
|
+
<div
|
|
419
|
+
class="wrapper"
|
|
420
|
+
part="wrapper"
|
|
421
|
+
>
|
|
422
|
+
<div
|
|
423
|
+
class="toolbar"
|
|
424
|
+
part="toolbar"
|
|
425
|
+
role="toolbar"
|
|
426
|
+
aria-label="Formatting"
|
|
427
|
+
>
|
|
428
|
+
<slot name="toolbar-start"></slot>
|
|
429
|
+
${this.editor ? map(this._toolbar, (command) => this._renderToolbarItem(command)) : null}
|
|
430
|
+
<slot name="toolbar-end"></slot>
|
|
431
|
+
</div>
|
|
432
|
+
<div
|
|
433
|
+
class="editor"
|
|
434
|
+
part="editor"
|
|
435
|
+
>
|
|
436
|
+
<slot name="editor"></slot>
|
|
437
|
+
</div>
|
|
438
|
+
</div>
|
|
439
|
+
`;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
_ProseEditor_initialHtml_accessor_storage = new WeakMap(), _ProseEditor_initialJson_accessor_storage = new WeakMap(), _ProseEditor_editorClass_accessor_storage = new WeakMap(), _ProseEditor_toolbar_accessor_storage = new WeakMap(), _ProseEditor_toolbarPreset_accessor_storage = new WeakMap(), _ProseEditor_toolbarPlacement_accessor_storage = new WeakMap(), _ProseEditor_autofocus_accessor_storage = new WeakMap(), _ProseEditor_placeholder_accessor_storage = new WeakMap(), _ProseEditor__emojiPickerActive_accessor_storage = new WeakMap();
|
|
443
|
+
ProseEditor.styles = [hostStyles, styles];
|
|
444
|
+
__decorate([
|
|
445
|
+
property({ attribute: 'initial-html' })
|
|
446
|
+
], ProseEditor.prototype, "initialHtml", null);
|
|
447
|
+
__decorate([
|
|
448
|
+
property({ attribute: 'initial-json' })
|
|
449
|
+
], ProseEditor.prototype, "initialJson", null);
|
|
450
|
+
__decorate([
|
|
451
|
+
property({ attribute: 'editor-class' })
|
|
452
|
+
], ProseEditor.prototype, "editorClass", null);
|
|
453
|
+
__decorate([
|
|
454
|
+
property({
|
|
455
|
+
converter: {
|
|
456
|
+
fromAttribute: (value) => (value ? value.split(',').map((s) => s.trim()) : []),
|
|
457
|
+
toAttribute: (value) => value.join(','),
|
|
458
|
+
},
|
|
459
|
+
})
|
|
460
|
+
], ProseEditor.prototype, "toolbar", null);
|
|
461
|
+
__decorate([
|
|
462
|
+
property({ attribute: 'toolbar-preset' })
|
|
463
|
+
], ProseEditor.prototype, "toolbarPreset", null);
|
|
464
|
+
__decorate([
|
|
465
|
+
property({ attribute: 'toolbar-placement', reflect: true })
|
|
466
|
+
], ProseEditor.prototype, "toolbarPlacement", null);
|
|
467
|
+
__decorate([
|
|
468
|
+
property({ type: Boolean, reflect: true })
|
|
469
|
+
], ProseEditor.prototype, "autofocus", null);
|
|
470
|
+
__decorate([
|
|
471
|
+
property()
|
|
472
|
+
], ProseEditor.prototype, "placeholder", null);
|
|
473
|
+
__decorate([
|
|
474
|
+
state()
|
|
475
|
+
], ProseEditor.prototype, "_emojiPickerActive", null);
|
package/dist/registry.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Every component base name the library ships. */
|
|
2
|
-
export type ElementBaseName = 'avatar' | 'badge' | 'carousel' | 'carousel-item' | 'dialog' | 'divider' | 'drawer' | 'dropdown' | 'dropdown-item' | 'icon' | 'input-otp' | 'input-stepper' | 'popover' | 'skeleton' | 'spinner' | 'rating' | 'sticky-bar' | 'stories' | 'story' | 'stories-viewer' | 'tabs' | 'toast' | 'toast-item' | 'tooltip' | 'tree' | 'tree-item';
|
|
2
|
+
export type ElementBaseName = 'avatar' | 'badge' | 'carousel' | 'carousel-item' | 'dialog' | 'divider' | 'drawer' | 'dropdown' | 'dropdown-item' | 'icon' | 'input-otp' | 'input-stepper' | 'popover' | 'prose-editor' | 'skeleton' | 'spinner' | 'rating' | 'sticky-bar' | 'stories' | 'story' | 'stories-viewer' | 'tabs' | 'toast' | 'toast-item' | 'tooltip' | 'tree' | 'tree-item';
|
|
3
3
|
/** Set the global prefix. Must be called before defining elements. */
|
|
4
4
|
export declare function setPrefix(prefix: {
|
|
5
5
|
element: string;
|
package/dist/registry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/html/registry.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,MAAM,MAAM,eAAe,GACvB,QAAQ,GACR,OAAO,GACP,UAAU,GACV,eAAe,GACf,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,UAAU,GACV,eAAe,GACf,MAAM,GACN,WAAW,GACX,eAAe,GACf,SAAS,GACT,UAAU,GACV,SAAS,GACT,QAAQ,GACR,YAAY,GACZ,SAAS,GACT,OAAO,GACP,gBAAgB,GAChB,MAAM,GACN,OAAO,GACP,YAAY,GACZ,SAAS,GACT,MAAM,GACN,WAAW,CAAC;AAMhB,sEAAsE;AACtE,wBAAgB,SAAS,CAAC,MAAM,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAGxE;AAED,gCAAgC;AAChC,wBAAgB,SAAS,IAAI;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAE5D;AAED,gEAAgE;AAChE,wBAAgB,OAAO,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,CAEzD;AAED,sEAAsE;AACtE,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExC;AAID,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED,kDAAkD;AAClD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAE/D;AAED,qEAAqE;AACrE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAE9D"}
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/html/registry.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,MAAM,MAAM,eAAe,GACvB,QAAQ,GACR,OAAO,GACP,UAAU,GACV,eAAe,GACf,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,UAAU,GACV,eAAe,GACf,MAAM,GACN,WAAW,GACX,eAAe,GACf,SAAS,GACT,cAAc,GACd,UAAU,GACV,SAAS,GACT,QAAQ,GACR,YAAY,GACZ,SAAS,GACT,OAAO,GACP,gBAAgB,GAChB,MAAM,GACN,OAAO,GACP,YAAY,GACZ,SAAS,GACT,MAAM,GACN,WAAW,CAAC;AAMhB,sEAAsE;AACtE,wBAAgB,SAAS,CAAC,MAAM,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAGxE;AAED,gCAAgC;AAChC,wBAAgB,SAAS,IAAI;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAE5D;AAED,gEAAgE;AAChE,wBAAgB,OAAO,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,CAEzD;AAED,sEAAsE;AACtE,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExC;AAID,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED,kDAAkD;AAClD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAE/D;AAED,qEAAqE;AACrE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAE9D"}
|