@37signals/lexxy 0.1.22-beta → 0.1.23-beta
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/README.md +1 -1
- package/dist/lexxy.esm.js +185 -168
- package/dist/stylesheets/lexxy-content.css +400 -0
- package/dist/stylesheets/lexxy-editor.css +451 -0
- package/dist/stylesheets/lexxy-variables.css +73 -0
- package/dist/stylesheets/lexxy.css +2 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ A modern rich text editor for Rails.
|
|
|
23
23
|
Add this line to your application's Gemfile:
|
|
24
24
|
|
|
25
25
|
```ruby
|
|
26
|
-
gem 'lexxy', '~> 0.1.
|
|
26
|
+
gem 'lexxy', '~> 0.1.22.beta' # Need to specify the version since it's a pre-release
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
And then execute:
|
package/dist/lexxy.esm.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import DOMPurify from 'dompurify';
|
|
2
2
|
import { getStyleObjectFromCSS, getCSSFromStyleObject, $getSelectionStyleValueForProperty, $patchStyleText } from '@lexical/selection';
|
|
3
|
-
import { $isTextNode, TextNode, $isRangeSelection, $getSelection, DecoratorNode, $getNodeByKey, HISTORY_MERGE_TAG, FORMAT_TEXT_COMMAND, $createTextNode, UNDO_COMMAND, REDO_COMMAND, PASTE_COMMAND, COMMAND_PRIORITY_LOW, $isNodeSelection, $getRoot, $isLineBreakNode, $isElementNode, KEY_ARROW_LEFT_COMMAND, KEY_ARROW_RIGHT_COMMAND, KEY_ARROW_UP_COMMAND, KEY_ARROW_DOWN_COMMAND, KEY_DELETE_COMMAND, KEY_BACKSPACE_COMMAND, SELECTION_CHANGE_COMMAND, $createNodeSelection, $setSelection, $createParagraphNode, KEY_ENTER_COMMAND, COMMAND_PRIORITY_HIGH, $isParagraphNode, $insertNodes, $createLineBreakNode, CLEAR_HISTORY_COMMAND, $addUpdateTag, SKIP_DOM_SELECTION_TAG, createEditor,
|
|
3
|
+
import { $isTextNode, TextNode, $isRangeSelection, $getSelection, DecoratorNode, $getNodeByKey, HISTORY_MERGE_TAG, FORMAT_TEXT_COMMAND, $createTextNode, UNDO_COMMAND, REDO_COMMAND, PASTE_COMMAND, COMMAND_PRIORITY_LOW, KEY_TAB_COMMAND, COMMAND_PRIORITY_NORMAL, OUTDENT_CONTENT_COMMAND, INDENT_CONTENT_COMMAND, $isNodeSelection, $getRoot, $isLineBreakNode, $isElementNode, KEY_ARROW_LEFT_COMMAND, KEY_ARROW_RIGHT_COMMAND, KEY_ARROW_UP_COMMAND, KEY_ARROW_DOWN_COMMAND, KEY_DELETE_COMMAND, KEY_BACKSPACE_COMMAND, SELECTION_CHANGE_COMMAND, $createNodeSelection, $setSelection, $createParagraphNode, KEY_ENTER_COMMAND, COMMAND_PRIORITY_HIGH, $isParagraphNode, $insertNodes, $createLineBreakNode, CLEAR_HISTORY_COMMAND, $addUpdateTag, SKIP_DOM_SELECTION_TAG, createEditor, BLUR_COMMAND, FOCUS_COMMAND, KEY_SPACE_COMMAND } from 'lexical';
|
|
4
4
|
import { $isListNode, $isListItemNode, INSERT_UNORDERED_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND, $createListNode, ListNode, ListItemNode, registerList } from '@lexical/list';
|
|
5
5
|
import { $isQuoteNode, $isHeadingNode, $createQuoteNode, $createHeadingNode, QuoteNode, HeadingNode, registerRichText } from '@lexical/rich-text';
|
|
6
6
|
import { $isCodeNode, CodeNode, normalizeCodeLang, CodeHighlightNode, registerCodeHighlighting, CODE_LANGUAGE_FRIENDLY_NAME_MAP } from '@lexical/code';
|
|
@@ -136,6 +136,8 @@ function hasHighlightStyles(cssOrStyles) {
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
class LexicalToolbarElement extends HTMLElement {
|
|
139
|
+
static observedAttributes = [ "connected" ]
|
|
140
|
+
|
|
139
141
|
constructor() {
|
|
140
142
|
super();
|
|
141
143
|
this.internals = this.attachInternals();
|
|
@@ -154,6 +156,13 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
154
156
|
this._resizeObserver.disconnect();
|
|
155
157
|
this._resizeObserver = null;
|
|
156
158
|
}
|
|
159
|
+
this.#unbindHotkeys();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
163
|
+
if (name === "connected" && this.isConnected && oldValue != null && oldValue !== newValue) {
|
|
164
|
+
requestAnimationFrame(() => this.#reconnect());
|
|
165
|
+
}
|
|
157
166
|
}
|
|
158
167
|
|
|
159
168
|
setEditor(editorElement) {
|
|
@@ -161,7 +170,8 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
161
170
|
this.editor = editorElement.editor;
|
|
162
171
|
this.#bindButtons();
|
|
163
172
|
this.#bindHotkeys();
|
|
164
|
-
this.#
|
|
173
|
+
this.#setTabIndexValues();
|
|
174
|
+
this.#setItemPositionValues();
|
|
165
175
|
this.#monitorSelectionChanges();
|
|
166
176
|
this.#monitorHistoryChanges();
|
|
167
177
|
this.#refreshToolbarOverflow();
|
|
@@ -169,10 +179,9 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
169
179
|
this.toggleAttribute("connected", true);
|
|
170
180
|
}
|
|
171
181
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
return Array.from(this.querySelectorAll(dialogTags))
|
|
182
|
+
#reconnect() {
|
|
183
|
+
this.disconnectedCallback();
|
|
184
|
+
this.connectedCallback();
|
|
176
185
|
}
|
|
177
186
|
|
|
178
187
|
#bindButtons() {
|
|
@@ -181,7 +190,6 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
181
190
|
|
|
182
191
|
#handleButtonClicked({ target }) {
|
|
183
192
|
this.#handleTargetClicked(target, "[data-command]", this.#dispatchButtonCommand.bind(this));
|
|
184
|
-
this.#handleTargetClicked(target, "[data-dialog-target]", this.#toggleDialog.bind(this));
|
|
185
193
|
}
|
|
186
194
|
|
|
187
195
|
#handleTargetClicked(target, selector, callback) {
|
|
@@ -196,38 +204,23 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
196
204
|
this.editor.dispatchCommand(command, payload);
|
|
197
205
|
}
|
|
198
206
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const dialogTarget = button.dataset.dialogTarget;
|
|
202
|
-
const dialog = this.querySelector("lexxy-" + dialogTarget);
|
|
203
|
-
if (!dialog) return
|
|
204
|
-
|
|
205
|
-
if (dialog.open) {
|
|
206
|
-
dialog.close();
|
|
207
|
-
} else {
|
|
208
|
-
this.#closeOpenDialogs();
|
|
209
|
-
dialog.show(button);
|
|
210
|
-
}
|
|
207
|
+
#bindHotkeys() {
|
|
208
|
+
this.editorElement.addEventListener("keydown", this.#handleHotkey);
|
|
211
209
|
}
|
|
212
210
|
|
|
213
|
-
#
|
|
214
|
-
|
|
215
|
-
openDialogs.forEach(openDialog => {
|
|
216
|
-
openDialog.closest(".lexxy-dialog").close();
|
|
217
|
-
});
|
|
211
|
+
#unbindHotkeys() {
|
|
212
|
+
this.editorElement?.removeEventListener("keydown", this.#handleHotkey);
|
|
218
213
|
}
|
|
219
214
|
|
|
220
|
-
#
|
|
221
|
-
this.
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}
|
|
230
|
-
});
|
|
215
|
+
#handleHotkey = (event) => {
|
|
216
|
+
const buttons = this.querySelectorAll("[data-hotkey]");
|
|
217
|
+
buttons.forEach((button) => {
|
|
218
|
+
const hotkeys = button.dataset.hotkey.toLowerCase().split(/\s+/);
|
|
219
|
+
if (hotkeys.includes(this.#keyCombinationFor(event))) {
|
|
220
|
+
event.preventDefault();
|
|
221
|
+
event.stopPropagation();
|
|
222
|
+
button.click();
|
|
223
|
+
}
|
|
231
224
|
});
|
|
232
225
|
}
|
|
233
226
|
|
|
@@ -243,10 +236,9 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
243
236
|
return [ ...modifiers, pressedKey ].join("+")
|
|
244
237
|
}
|
|
245
238
|
|
|
246
|
-
#
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
button.setAttribute("tabindex", `${baseTabIndex + index + 1}`);
|
|
239
|
+
#setTabIndexValues() {
|
|
240
|
+
this.#buttons.forEach((button) => {
|
|
241
|
+
button.setAttribute("tabindex", 0);
|
|
250
242
|
});
|
|
251
243
|
}
|
|
252
244
|
|
|
@@ -254,7 +246,6 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
254
246
|
this.editor.registerUpdateListener(() => {
|
|
255
247
|
this.editor.getEditorState().read(() => {
|
|
256
248
|
this.#updateButtonStates();
|
|
257
|
-
this.#updateDialogStates();
|
|
258
249
|
});
|
|
259
250
|
});
|
|
260
251
|
}
|
|
@@ -309,10 +300,6 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
309
300
|
this.#updateUndoRedoButtonStates();
|
|
310
301
|
}
|
|
311
302
|
|
|
312
|
-
#updateDialogStates() {
|
|
313
|
-
this.#dialogs.forEach(dialog => dialog.updateStateCallback());
|
|
314
|
-
}
|
|
315
|
-
|
|
316
303
|
#isInList(node) {
|
|
317
304
|
let current = node;
|
|
318
305
|
while (current) {
|
|
@@ -361,22 +348,8 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
361
348
|
this.toggleAttribute("overflowing", isOverflowing);
|
|
362
349
|
}
|
|
363
350
|
|
|
364
|
-
get #overflow() {
|
|
365
|
-
return this.querySelector(".lexxy-editor__toolbar-overflow")
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
get #overflowMenu() {
|
|
369
|
-
return this.querySelector(".lexxy-editor__toolbar-overflow-menu")
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
#resetToolbar() {
|
|
373
|
-
while (this.#overflowMenu.children.length > 0) {
|
|
374
|
-
this.insertBefore(this.#overflowMenu.children[0], this.#overflow);
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
|
|
378
351
|
#compactMenu() {
|
|
379
|
-
const buttons = this.#
|
|
352
|
+
const buttons = this.#buttons.reverse();
|
|
380
353
|
let movedToOverflow = false;
|
|
381
354
|
|
|
382
355
|
for (const button of buttons) {
|
|
@@ -390,12 +363,42 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
390
363
|
}
|
|
391
364
|
}
|
|
392
365
|
|
|
366
|
+
#resetToolbar() {
|
|
367
|
+
const items = Array.from(this.#overflowMenu.children);
|
|
368
|
+
items.sort((a, b) => this.#itemPosition(b) - this.#itemPosition(a));
|
|
369
|
+
|
|
370
|
+
items.forEach((item) => {
|
|
371
|
+
const nextItem = this.querySelector(`[data-position="${this.#itemPosition(item) + 1}"]`) ?? this.#overflow;
|
|
372
|
+
this.insertBefore(item, nextItem);
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
#itemPosition(item) {
|
|
377
|
+
return parseInt(item.dataset.position ?? "999")
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
#setItemPositionValues() {
|
|
381
|
+
this.#toolbarItems.forEach((item, index) => {
|
|
382
|
+
if (item.dataset.position === undefined) {
|
|
383
|
+
item.dataset.position = index;
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
get #overflow() {
|
|
389
|
+
return this.querySelector(".lexxy-editor__toolbar-overflow")
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
get #overflowMenu() {
|
|
393
|
+
return this.querySelector(".lexxy-editor__toolbar-overflow-menu")
|
|
394
|
+
}
|
|
395
|
+
|
|
393
396
|
get #buttons() {
|
|
394
397
|
return Array.from(this.querySelectorAll(":scope > button"))
|
|
395
398
|
}
|
|
396
399
|
|
|
397
|
-
get #
|
|
398
|
-
return Array.from(this.querySelectorAll(":scope >
|
|
400
|
+
get #toolbarItems() {
|
|
401
|
+
return Array.from(this.querySelectorAll(":scope > *:not(.lexxy-editor__toolbar-overflow)"))
|
|
399
402
|
}
|
|
400
403
|
|
|
401
404
|
static get defaultTemplate() {
|
|
@@ -414,35 +417,31 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
414
417
|
</svg>
|
|
415
418
|
</button>
|
|
416
419
|
|
|
417
|
-
<
|
|
418
|
-
<
|
|
419
|
-
<
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
<button class="lexxy-editor__toolbar-button" type="button" name="highlight" title="Color highlight" data-dialog-target="highlight-dialog">
|
|
428
|
-
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M7.65422 0.711575C7.1856 0.242951 6.42579 0.242951 5.95717 0.711575C5.48853 1.18021 5.48853 1.94 5.95717 2.40864L8.70864 5.16011L2.85422 11.0145C1.44834 12.4204 1.44833 14.6998 2.85422 16.1057L7.86011 21.1115C9.26599 22.5174 11.5454 22.5174 12.9513 21.1115L19.6542 14.4087C20.1228 13.94 20.1228 13.1802 19.6542 12.7115L11.8544 4.91171L11.2542 4.31158L7.65422 0.711575ZM4.55127 12.7115L10.4057 6.85716L17.1087 13.56H4.19981C4.19981 13.253 4.31696 12.9459 4.55127 12.7115ZM23.6057 20.76C23.6057 22.0856 22.5311 23.16 21.2057 23.16C19.8802 23.16 18.8057 22.0856 18.8057 20.76C18.8057 19.5408 19.8212 18.5339 20.918 17.4462C21.0135 17.3516 21.1096 17.2563 21.2057 17.16C21.3018 17.2563 21.398 17.3516 21.4935 17.4462C22.5903 18.5339 23.6057 19.5408 23.6057 20.76Z"/></svg>
|
|
429
|
-
</button>
|
|
420
|
+
<details class="lexxy-editor__toolbar-dropdown" name="lexxy-dropdown">
|
|
421
|
+
<summary class="lexxy-editor__toolbar-button" name="highlight" title="Color highlight">
|
|
422
|
+
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M7.65422 0.711575C7.1856 0.242951 6.42579 0.242951 5.95717 0.711575C5.48853 1.18021 5.48853 1.94 5.95717 2.40864L8.70864 5.16011L2.85422 11.0145C1.44834 12.4204 1.44833 14.6998 2.85422 16.1057L7.86011 21.1115C9.26599 22.5174 11.5454 22.5174 12.9513 21.1115L19.6542 14.4087C20.1228 13.94 20.1228 13.1802 19.6542 12.7115L11.8544 4.91171L11.2542 4.31158L7.65422 0.711575ZM4.55127 12.7115L10.4057 6.85716L17.1087 13.56H4.19981C4.19981 13.253 4.31696 12.9459 4.55127 12.7115ZM23.6057 20.76C23.6057 22.0856 22.5311 23.16 21.2057 23.16C19.8802 23.16 18.8057 22.0856 18.8057 20.76C18.8057 19.5408 19.8212 18.5339 20.918 17.4462C21.0135 17.3516 21.1096 17.2563 21.2057 17.16C21.3018 17.2563 21.398 17.3516 21.4935 17.4462C22.5903 18.5339 23.6057 19.5408 23.6057 20.76Z"/></svg>
|
|
423
|
+
</summary>
|
|
424
|
+
<lexxy-highlight-dropdown class="lexxy-editor__toolbar-dropdown-content">
|
|
425
|
+
<div data-button-group="color" data-values="var(--highlight-1); var(--highlight-2); var(--highlight-3); var(--highlight-4); var(--highlight-5); var(--highlight-6); var(--highlight-7); var(--highlight-8); var(--highlight-9)"></div>
|
|
426
|
+
<div data-button-group="background-color" data-values="var(--highlight-bg-1); var(--highlight-bg-2); var(--highlight-bg-3); var(--highlight-bg-4); var(--highlight-bg-5); var(--highlight-bg-6); var(--highlight-bg-7); var(--highlight-bg-8); var(--highlight-bg-9)"></div>
|
|
427
|
+
<button data-command="removeHighlight" class="lexxy-editor__toolbar-dropdown-reset">Remove all coloring</button>
|
|
428
|
+
</lexxy-highlight-dropdown>
|
|
429
|
+
</details>
|
|
430
430
|
|
|
431
|
-
<
|
|
432
|
-
<
|
|
431
|
+
<details class="lexxy-editor__toolbar-dropdown" name="lexxy-dropdown">
|
|
432
|
+
<summary class="lexxy-editor__toolbar-button" name="link" title="Link" data-hotkey="cmd+k ctrl+k">
|
|
433
|
+
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12.111 9.546a1.5 1.5 0 012.121 0 5.5 5.5 0 010 7.778l-2.828 2.828a5.5 5.5 0 01-7.778 0 5.498 5.498 0 010-7.777l2.828-2.83a1.5 1.5 0 01.355-.262 6.52 6.52 0 00.351 3.799l-1.413 1.414a2.499 2.499 0 000 3.535 2.499 2.499 0 003.535 0l2.83-2.828a2.5 2.5 0 000-3.536 1.5 1.5 0 010-2.121z"/><path d="M12.111 3.89a5.5 5.5 0 117.778 7.777l-2.828 2.829a1.496 1.496 0 01-.355.262 6.522 6.522 0 00-.351-3.8l1.413-1.412a2.5 2.5 0 10-3.536-3.535l-2.828 2.828a2.5 2.5 0 000 3.536 1.5 1.5 0 01-2.122 2.12 5.5 5.5 0 010-7.777l2.83-2.829z"/></svg>
|
|
434
|
+
</summary>
|
|
435
|
+
<lexxy-link-dropdown class="lexxy-editor__toolbar-dropdown-content">
|
|
433
436
|
<form method="dialog">
|
|
434
|
-
<input type="url" placeholder="Enter a URL…" class="input"
|
|
435
|
-
<div class="lexxy-
|
|
437
|
+
<input type="url" placeholder="Enter a URL…" class="input">
|
|
438
|
+
<div class="lexxy-editor__toolbar-dropdown-actions">
|
|
436
439
|
<button type="submit" class="btn" value="link">Link</button>
|
|
437
440
|
<button type="button" class="btn" value="unlink">Unlink</button>
|
|
438
441
|
</div>
|
|
439
442
|
</form>
|
|
440
|
-
</
|
|
441
|
-
</
|
|
442
|
-
|
|
443
|
-
<button class="lexxy-editor__toolbar-button" type="button" name="link" title="Link" data-dialog-target="link-dialog" data-hotkey="cmd+k ctrl+k">
|
|
444
|
-
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12.111 9.546a1.5 1.5 0 012.121 0 5.5 5.5 0 010 7.778l-2.828 2.828a5.5 5.5 0 01-7.778 0 5.498 5.498 0 010-7.777l2.828-2.83a1.5 1.5 0 01.355-.262 6.52 6.52 0 00.351 3.799l-1.413 1.414a2.499 2.499 0 000 3.535 2.499 2.499 0 003.535 0l2.83-2.828a2.5 2.5 0 000-3.536 1.5 1.5 0 010-2.121z"/><path d="M12.111 3.89a5.5 5.5 0 117.778 7.777l-2.828 2.829a1.496 1.496 0 01-.355.262 6.522 6.522 0 00-.351-3.8l1.413-1.412a2.5 2.5 0 10-3.536-3.535l-2.828 2.828a2.5 2.5 0 000 3.536 1.5 1.5 0 01-2.122 2.12 5.5 5.5 0 010-7.777l2.83-2.829z"/></svg>
|
|
445
|
-
</button>
|
|
443
|
+
</lexxy-link-dropdown>
|
|
444
|
+
</details>
|
|
446
445
|
|
|
447
446
|
<button class="lexxy-editor__toolbar-button" type="button" name="quote" data-command="insertQuoteBlock" title="Quote">
|
|
448
447
|
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M6.5 5C8.985 5 11 7.09 11 9.667c0 2.694-.962 5.005-2.187 6.644-.613.82-1.3 1.481-1.978 1.943-.668.454-1.375.746-2.022.746a.563.563 0 01-.52-.36.602.602 0 01.067-.57l.055-.066.009-.009.041-.048a4.25 4.25 0 00.168-.21c.143-.188.336-.47.53-.84a6.743 6.743 0 00.75-2.605C3.705 13.994 2 12.038 2 9.667 2 7.089 4.015 5 6.5 5zM17.5 5C19.985 5 22 7.09 22 9.667c0 2.694-.962 5.005-2.187 6.644-.613.82-1.3 1.481-1.978 1.943-.668.454-1.375.746-2.023.746a.563.563 0 01-.52-.36.602.602 0 01.068-.57l.055-.066.009-.009.041-.048c.039-.045.097-.115.168-.21a6.16 6.16 0 00.53-.84 6.745 6.745 0 00.75-2.605C14.705 13.994 13 12.038 13 9.667 13 7.089 15.015 5 17.5 5z"/></svg>
|
|
@@ -1136,6 +1135,7 @@ class CommandDispatcher {
|
|
|
1136
1135
|
this.highlighter = editorElement.highlighter;
|
|
1137
1136
|
|
|
1138
1137
|
this.#registerCommands();
|
|
1138
|
+
this.#registerKeyboardCommands();
|
|
1139
1139
|
this.#registerDragAndDropHandlers();
|
|
1140
1140
|
}
|
|
1141
1141
|
|
|
@@ -1300,15 +1300,8 @@ class CommandDispatcher {
|
|
|
1300
1300
|
this.editor.registerCommand(command, handler, priority);
|
|
1301
1301
|
}
|
|
1302
1302
|
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
this.editor.update(() => {
|
|
1306
|
-
if (url === null) {
|
|
1307
|
-
$toggleLink(null);
|
|
1308
|
-
} else {
|
|
1309
|
-
$toggleLink(url);
|
|
1310
|
-
}
|
|
1311
|
-
});
|
|
1303
|
+
#registerKeyboardCommands() {
|
|
1304
|
+
this.editor.registerCommand(KEY_TAB_COMMAND, this.#handleListIndentation.bind(this), COMMAND_PRIORITY_NORMAL);
|
|
1312
1305
|
}
|
|
1313
1306
|
|
|
1314
1307
|
#registerDragAndDropHandlers() {
|
|
@@ -1357,6 +1350,29 @@ class CommandDispatcher {
|
|
|
1357
1350
|
|
|
1358
1351
|
this.editor.focus();
|
|
1359
1352
|
}
|
|
1353
|
+
|
|
1354
|
+
#handleListIndentation(event) {
|
|
1355
|
+
if (this.selection.isInsideList) {
|
|
1356
|
+
event.preventDefault();
|
|
1357
|
+
if (event.shiftKey) {
|
|
1358
|
+
return this.editor.dispatchCommand(OUTDENT_CONTENT_COMMAND, undefined)
|
|
1359
|
+
} else {
|
|
1360
|
+
return this.editor.dispatchCommand(INDENT_CONTENT_COMMAND, undefined)
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
return false
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
// Not using TOGGLE_LINK_COMMAND because it's not handled unless you use React/LinkPlugin
|
|
1367
|
+
#toggleLink(url) {
|
|
1368
|
+
this.editor.update(() => {
|
|
1369
|
+
if (url === null) {
|
|
1370
|
+
$toggleLink(null);
|
|
1371
|
+
} else {
|
|
1372
|
+
$toggleLink(url);
|
|
1373
|
+
}
|
|
1374
|
+
});
|
|
1375
|
+
}
|
|
1360
1376
|
}
|
|
1361
1377
|
|
|
1362
1378
|
function capitalize(str) {
|
|
@@ -3475,6 +3491,10 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
3475
3491
|
return this.getAttribute("attachments") !== "false"
|
|
3476
3492
|
}
|
|
3477
3493
|
|
|
3494
|
+
get contentTabIndex() {
|
|
3495
|
+
return parseInt(this.editorContentElement?.getAttribute("tabindex") ?? "0")
|
|
3496
|
+
}
|
|
3497
|
+
|
|
3478
3498
|
focus() {
|
|
3479
3499
|
this.editor.focus();
|
|
3480
3500
|
}
|
|
@@ -3788,39 +3808,26 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
3788
3808
|
|
|
3789
3809
|
#reconnect() {
|
|
3790
3810
|
this.disconnectedCallback();
|
|
3811
|
+
this.valueBeforeDisconnect = null;
|
|
3791
3812
|
this.connectedCallback();
|
|
3792
3813
|
}
|
|
3793
3814
|
}
|
|
3794
3815
|
|
|
3795
3816
|
customElements.define("lexxy-editor", LexicalEditorElement);
|
|
3796
3817
|
|
|
3797
|
-
class
|
|
3818
|
+
class ToolbarDropdown extends HTMLElement {
|
|
3798
3819
|
connectedCallback() {
|
|
3799
|
-
this.
|
|
3800
|
-
if ("closedBy" in this.dialog.constructor.prototype) {
|
|
3801
|
-
this.dialog.closedBy = "any";
|
|
3802
|
-
}
|
|
3803
|
-
this.#registerHandlers();
|
|
3804
|
-
}
|
|
3805
|
-
|
|
3806
|
-
disconnectedCallback() {
|
|
3807
|
-
this.#removeClickOutsideHandler();
|
|
3808
|
-
}
|
|
3809
|
-
|
|
3810
|
-
updateStateCallback() { }
|
|
3820
|
+
this.container = this.closest("details");
|
|
3811
3821
|
|
|
3812
|
-
|
|
3813
|
-
|
|
3822
|
+
this.container.addEventListener("toggle", this.#handleToggle.bind(this));
|
|
3823
|
+
this.container.addEventListener("keydown", this.#handleKeyDown.bind(this));
|
|
3814
3824
|
|
|
3815
|
-
this
|
|
3816
|
-
this.#positionDialog();
|
|
3817
|
-
this.dialog.show();
|
|
3818
|
-
|
|
3819
|
-
this.#setupClickOutsideHandler();
|
|
3825
|
+
this.#setTabIndexValues();
|
|
3820
3826
|
}
|
|
3821
3827
|
|
|
3822
|
-
|
|
3823
|
-
this
|
|
3828
|
+
disconnectedCallback() {
|
|
3829
|
+
this.#removeClickOutsideHandler();
|
|
3830
|
+
this.container.removeEventListener("keydown", this.#handleKeyDown.bind(this));
|
|
3824
3831
|
}
|
|
3825
3832
|
|
|
3826
3833
|
get toolbar() {
|
|
@@ -3831,32 +3838,32 @@ class ToolbarDialog extends HTMLElement {
|
|
|
3831
3838
|
return this.toolbar.editor
|
|
3832
3839
|
}
|
|
3833
3840
|
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
#registerHandlers() {
|
|
3837
|
-
this.#setupKeydownHandler();
|
|
3838
|
-
this.dialog.addEventListener("cancel", this.#handleCancel.bind(this));
|
|
3839
|
-
this.dialog.addEventListener("close", this.#handleClose.bind(this));
|
|
3841
|
+
close() {
|
|
3842
|
+
this.container.removeAttribute("open");
|
|
3840
3843
|
}
|
|
3841
3844
|
|
|
3842
|
-
#
|
|
3843
|
-
this
|
|
3844
|
-
|
|
3845
|
-
|
|
3845
|
+
#handleToggle(event) {
|
|
3846
|
+
if (this.container.open) {
|
|
3847
|
+
this.#handleOpen(event.target);
|
|
3848
|
+
} else {
|
|
3849
|
+
this.#handleClose();
|
|
3850
|
+
}
|
|
3846
3851
|
}
|
|
3847
3852
|
|
|
3848
|
-
#
|
|
3849
|
-
this.
|
|
3850
|
-
|
|
3853
|
+
#handleOpen(trigger) {
|
|
3854
|
+
this.trigger = trigger;
|
|
3855
|
+
this.#interactiveElements[0].focus();
|
|
3856
|
+
this.#setupClickOutsideHandler();
|
|
3851
3857
|
}
|
|
3852
3858
|
|
|
3853
|
-
#
|
|
3854
|
-
|
|
3855
|
-
this
|
|
3859
|
+
#handleClose() {
|
|
3860
|
+
this.trigger = null;
|
|
3861
|
+
this.#removeClickOutsideHandler();
|
|
3862
|
+
this.editor.focus();
|
|
3856
3863
|
}
|
|
3857
3864
|
|
|
3858
3865
|
#setupClickOutsideHandler() {
|
|
3859
|
-
if (this
|
|
3866
|
+
if (this.clickOutsideHandler) return
|
|
3860
3867
|
|
|
3861
3868
|
this.clickOutsideHandler = this.#handleClickOutside.bind(this);
|
|
3862
3869
|
document.addEventListener("click", this.clickOutsideHandler, true);
|
|
@@ -3870,22 +3877,7 @@ class ToolbarDialog extends HTMLElement {
|
|
|
3870
3877
|
}
|
|
3871
3878
|
|
|
3872
3879
|
#handleClickOutside({ target }) {
|
|
3873
|
-
if (!this.
|
|
3874
|
-
|
|
3875
|
-
const isClickInsideDialog = this.dialog.contains(target);
|
|
3876
|
-
const isClickOnTrigger = this.triggerButton.contains(target);
|
|
3877
|
-
|
|
3878
|
-
if (!isClickInsideDialog && !isClickOnTrigger) {
|
|
3879
|
-
this.close();
|
|
3880
|
-
}
|
|
3881
|
-
}
|
|
3882
|
-
|
|
3883
|
-
#setupKeydownHandler() {
|
|
3884
|
-
if (!this.#browserHandlesClose) { this.addEventListener("keydown", this.#handleKeyDown.bind(this)); }
|
|
3885
|
-
}
|
|
3886
|
-
|
|
3887
|
-
get #browserHandlesClose() {
|
|
3888
|
-
return this.dialog.closedBy === "any"
|
|
3880
|
+
if (this.container.open && !this.container.contains(target)) this.close();
|
|
3889
3881
|
}
|
|
3890
3882
|
|
|
3891
3883
|
#handleKeyDown(event) {
|
|
@@ -3894,9 +3886,20 @@ class ToolbarDialog extends HTMLElement {
|
|
|
3894
3886
|
this.close();
|
|
3895
3887
|
}
|
|
3896
3888
|
}
|
|
3889
|
+
|
|
3890
|
+
async #setTabIndexValues() {
|
|
3891
|
+
await nextFrame();
|
|
3892
|
+
this.#interactiveElements.forEach((element) => {
|
|
3893
|
+
element.setAttribute("tabindex", 0);
|
|
3894
|
+
});
|
|
3895
|
+
}
|
|
3896
|
+
|
|
3897
|
+
get #interactiveElements() {
|
|
3898
|
+
return Array.from(this.querySelectorAll("button, input"))
|
|
3899
|
+
}
|
|
3897
3900
|
}
|
|
3898
3901
|
|
|
3899
|
-
class
|
|
3902
|
+
class LinkDropdown extends ToolbarDropdown {
|
|
3900
3903
|
connectedCallback() {
|
|
3901
3904
|
super.connectedCallback();
|
|
3902
3905
|
this.input = this.querySelector("input");
|
|
@@ -3904,23 +3907,21 @@ class LinkDialog extends ToolbarDialog {
|
|
|
3904
3907
|
this.#registerHandlers();
|
|
3905
3908
|
}
|
|
3906
3909
|
|
|
3907
|
-
updateStateCallback() {
|
|
3908
|
-
this.input.value = this.#selectedLinkUrl;
|
|
3909
|
-
}
|
|
3910
|
-
|
|
3911
3910
|
#registerHandlers() {
|
|
3912
|
-
this.
|
|
3913
|
-
this.
|
|
3911
|
+
this.container.addEventListener("toggle", this.#handleToggle.bind(this));
|
|
3912
|
+
this.addEventListener("submit", this.#handleSubmit.bind(this));
|
|
3914
3913
|
this.querySelector("[value='unlink']").addEventListener("click", this.#handleUnlink.bind(this));
|
|
3915
3914
|
}
|
|
3916
3915
|
|
|
3917
|
-
#
|
|
3916
|
+
#handleToggle({ newState }) {
|
|
3917
|
+
this.input.value = this.#selectedLinkUrl;
|
|
3918
3918
|
this.input.required = newState === "open";
|
|
3919
3919
|
}
|
|
3920
3920
|
|
|
3921
3921
|
#handleSubmit(event) {
|
|
3922
3922
|
const command = event.submitter?.value;
|
|
3923
3923
|
this.editor.dispatchCommand(command, this.input.value);
|
|
3924
|
+
this.close();
|
|
3924
3925
|
}
|
|
3925
3926
|
|
|
3926
3927
|
#handleUnlink() {
|
|
@@ -3949,9 +3950,7 @@ class LinkDialog extends ToolbarDialog {
|
|
|
3949
3950
|
}
|
|
3950
3951
|
}
|
|
3951
3952
|
|
|
3952
|
-
|
|
3953
|
-
// supported by Safari yet: customElements.define("lexxy-link-dialog", LinkDialog, { extends: "dialog" })
|
|
3954
|
-
customElements.define("lexxy-link-dialog", LinkDialog);
|
|
3953
|
+
customElements.define("lexxy-link-dropdown", LinkDropdown);
|
|
3955
3954
|
|
|
3956
3955
|
const APPLY_HIGHLIGHT_SELECTOR = "button.lexxy-highlight-button";
|
|
3957
3956
|
const REMOVE_HIGHLIGHT_SELECTOR = "[data-command='removeHighlight']";
|
|
@@ -3961,7 +3960,7 @@ const REMOVE_HIGHLIGHT_SELECTOR = "[data-command='removeHighlight']";
|
|
|
3961
3960
|
// see https://github.com/facebook/lexical/issues/8013
|
|
3962
3961
|
const NO_STYLE = Symbol("no_style");
|
|
3963
3962
|
|
|
3964
|
-
class
|
|
3963
|
+
class HighlightDropdown extends ToolbarDropdown {
|
|
3965
3964
|
connectedCallback() {
|
|
3966
3965
|
super.connectedCallback();
|
|
3967
3966
|
|
|
@@ -3969,13 +3968,10 @@ class HighlightDialog extends ToolbarDialog {
|
|
|
3969
3968
|
this.#registerHandlers();
|
|
3970
3969
|
}
|
|
3971
3970
|
|
|
3972
|
-
updateStateCallback() {
|
|
3973
|
-
this.#updateColorButtonStates($getSelection());
|
|
3974
|
-
}
|
|
3975
|
-
|
|
3976
3971
|
#registerHandlers() {
|
|
3977
|
-
this.
|
|
3972
|
+
this.container.addEventListener("toggle", this.#handleToggle.bind(this));
|
|
3978
3973
|
this.#colorButtons.forEach(button => button.addEventListener("click", this.#handleColorButtonClick.bind(this)));
|
|
3974
|
+
this.querySelector(REMOVE_HIGHLIGHT_SELECTOR).addEventListener("click", this.#handleRemoveHighlightClick.bind(this));
|
|
3979
3975
|
}
|
|
3980
3976
|
|
|
3981
3977
|
#setUpButtons() {
|
|
@@ -4002,6 +3998,14 @@ class HighlightDialog extends ToolbarDialog {
|
|
|
4002
3998
|
return button
|
|
4003
3999
|
}
|
|
4004
4000
|
|
|
4001
|
+
#handleToggle({ newState }) {
|
|
4002
|
+
if (newState === "open") {
|
|
4003
|
+
this.editor.getEditorState().read(() => {
|
|
4004
|
+
this.#updateColorButtonStates($getSelection());
|
|
4005
|
+
});
|
|
4006
|
+
}
|
|
4007
|
+
}
|
|
4008
|
+
|
|
4005
4009
|
#handleColorButtonClick(event) {
|
|
4006
4010
|
event.preventDefault();
|
|
4007
4011
|
|
|
@@ -4047,9 +4051,7 @@ class HighlightDialog extends ToolbarDialog {
|
|
|
4047
4051
|
}
|
|
4048
4052
|
}
|
|
4049
4053
|
|
|
4050
|
-
|
|
4051
|
-
// supported by Safari yet: customElements.define("lexxy-hightlight-dialog", HighlightDialog, { extends: "dialog" })
|
|
4052
|
-
customElements.define("lexxy-highlight-dialog", HighlightDialog);
|
|
4054
|
+
customElements.define("lexxy-highlight-dropdown", HighlightDropdown);
|
|
4053
4055
|
|
|
4054
4056
|
class BaseSource {
|
|
4055
4057
|
// Template method to override
|
|
@@ -4194,10 +4196,13 @@ class LexicalPromptElement extends HTMLElement {
|
|
|
4194
4196
|
this.keyListeners = [];
|
|
4195
4197
|
}
|
|
4196
4198
|
|
|
4199
|
+
static observedAttributes = [ "connected" ]
|
|
4200
|
+
|
|
4197
4201
|
connectedCallback() {
|
|
4198
4202
|
this.source = this.#createSource();
|
|
4199
4203
|
|
|
4200
4204
|
this.#addTriggerListener();
|
|
4205
|
+
this.toggleAttribute("connected", true);
|
|
4201
4206
|
}
|
|
4202
4207
|
|
|
4203
4208
|
disconnectedCallback() {
|
|
@@ -4205,6 +4210,13 @@ class LexicalPromptElement extends HTMLElement {
|
|
|
4205
4210
|
this.popoverElement = null;
|
|
4206
4211
|
}
|
|
4207
4212
|
|
|
4213
|
+
|
|
4214
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
4215
|
+
if (name === "connected" && this.isConnected && oldValue != null && oldValue !== newValue) {
|
|
4216
|
+
requestAnimationFrame(() => this.#reconnect());
|
|
4217
|
+
}
|
|
4218
|
+
}
|
|
4219
|
+
|
|
4208
4220
|
get name() {
|
|
4209
4221
|
return this.getAttribute("name")
|
|
4210
4222
|
}
|
|
@@ -4574,6 +4586,11 @@ class LexicalPromptElement extends HTMLElement {
|
|
|
4574
4586
|
this.#optionWasSelected();
|
|
4575
4587
|
}
|
|
4576
4588
|
}
|
|
4589
|
+
|
|
4590
|
+
#reconnect() {
|
|
4591
|
+
this.disconnectedCallback();
|
|
4592
|
+
this.connectedCallback();
|
|
4593
|
+
}
|
|
4577
4594
|
}
|
|
4578
4595
|
|
|
4579
4596
|
customElements.define("lexxy-prompt", LexicalPromptElement);
|