@37signals/lexxy 0.1.25-beta → 0.1.26-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/dist/lexxy.esm.js +222 -39
- package/dist/stylesheets/lexxy-editor.css +12 -13
- package/package.json +2 -2
package/dist/lexxy.esm.js
CHANGED
|
@@ -10,8 +10,8 @@ import 'prismjs/components/prism-json';
|
|
|
10
10
|
import 'prismjs/components/prism-diff';
|
|
11
11
|
import DOMPurify from 'dompurify';
|
|
12
12
|
import { getStyleObjectFromCSS, getCSSFromStyleObject, $getSelectionStyleValueForProperty, $patchStyleText } from '@lexical/selection';
|
|
13
|
-
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,
|
|
14
|
-
import { $isListNode, $isListItemNode, INSERT_UNORDERED_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND, $
|
|
13
|
+
import { $isTextNode, TextNode, $isRangeSelection, SKIP_DOM_SELECTION_TAG, $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, createEditor, BLUR_COMMAND, FOCUS_COMMAND, KEY_DOWN_COMMAND, KEY_SPACE_COMMAND } from 'lexical';
|
|
14
|
+
import { $isListNode, $isListItemNode, INSERT_UNORDERED_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND, ListNode, $getListDepth, $createListNode, ListItemNode, registerList } from '@lexical/list';
|
|
15
15
|
import { $isQuoteNode, $isHeadingNode, $createQuoteNode, $createHeadingNode, QuoteNode, HeadingNode, registerRichText } from '@lexical/rich-text';
|
|
16
16
|
import { $isCodeNode, CodeNode, normalizeCodeLang, CodeHighlightNode, registerCodeHighlighting, CODE_LANGUAGE_FRIENDLY_NAME_MAP } from '@lexical/code';
|
|
17
17
|
import { $isLinkNode, $createAutoLinkNode, $toggleLink, $createLinkNode, LinkNode, AutoLinkNode } from '@lexical/link';
|
|
@@ -20,6 +20,7 @@ import { $generateNodesFromDOM, $generateHtmlFromNodes } from '@lexical/html';
|
|
|
20
20
|
import { registerMarkdownShortcuts, TRANSFORMERS } from '@lexical/markdown';
|
|
21
21
|
import { createEmptyHistoryState, registerHistory } from '@lexical/history';
|
|
22
22
|
import { DirectUpload } from '@rails/activestorage';
|
|
23
|
+
import { $getNearestNodeOfType } from '@lexical/utils';
|
|
23
24
|
import { marked } from 'marked';
|
|
24
25
|
|
|
25
26
|
// Configure Prism for manual highlighting mode
|
|
@@ -145,6 +146,93 @@ function hasHighlightStyles(cssOrStyles) {
|
|
|
145
146
|
return !!(styles.color || styles["background-color"])
|
|
146
147
|
}
|
|
147
148
|
|
|
149
|
+
function handleRollingTabIndex(elements, event) {
|
|
150
|
+
const previousActiveElement = document.activeElement;
|
|
151
|
+
|
|
152
|
+
if (elements.includes(previousActiveElement)) {
|
|
153
|
+
const finder = new NextElementFinder(elements, event.key);
|
|
154
|
+
|
|
155
|
+
if (finder.selectNext(previousActiveElement)) {
|
|
156
|
+
event.preventDefault();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
class NextElementFinder {
|
|
162
|
+
constructor(elements, key) {
|
|
163
|
+
this.elements = elements;
|
|
164
|
+
this.key = key;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
selectNext(fromElement) {
|
|
168
|
+
const nextElement = this.#findNextElement(fromElement);
|
|
169
|
+
|
|
170
|
+
if (nextElement) {
|
|
171
|
+
const inactiveElements = this.elements.filter(element => element !== nextElement);
|
|
172
|
+
this.#unsetTabIndex(inactiveElements);
|
|
173
|
+
this.#focusWithActiveTabIndex(nextElement);
|
|
174
|
+
return true
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return false
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
#findNextElement(fromElement) {
|
|
181
|
+
switch (this.key) {
|
|
182
|
+
case "ArrowRight":
|
|
183
|
+
case "ArrowDown":
|
|
184
|
+
return this.#findNextSibling(fromElement)
|
|
185
|
+
|
|
186
|
+
case "ArrowLeft":
|
|
187
|
+
case "ArrowUp":
|
|
188
|
+
return this.#findPreviousSibling(fromElement)
|
|
189
|
+
|
|
190
|
+
case "Home":
|
|
191
|
+
return this.#findFirst()
|
|
192
|
+
|
|
193
|
+
case "End":
|
|
194
|
+
return this.#findLast()
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
#findFirst(elements = this.elements) {
|
|
199
|
+
return elements.find(isActiveAndVisible)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
#findLast(elements = this.elements) {
|
|
203
|
+
return elements.findLast(isActiveAndVisible)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
#findNextSibling(element) {
|
|
207
|
+
const afterElements = this.elements.slice(this.#indexOf(element) + 1);
|
|
208
|
+
return this.#findFirst(afterElements)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
#findPreviousSibling(element) {
|
|
212
|
+
const beforeElements = this.elements.slice(0, this.#indexOf(element));
|
|
213
|
+
return this.#findLast(beforeElements)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
#indexOf(element) {
|
|
217
|
+
return this.elements.indexOf(element)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
#focusWithActiveTabIndex(element) {
|
|
221
|
+
if (isActiveAndVisible(element)) {
|
|
222
|
+
element.tabIndex = 0;
|
|
223
|
+
element.focus();
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
#unsetTabIndex(elements) {
|
|
228
|
+
elements.forEach(element => element.tabIndex = -1);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function isActiveAndVisible(element) {
|
|
233
|
+
return element && !element.disabled && element.checkVisibility()
|
|
234
|
+
}
|
|
235
|
+
|
|
148
236
|
class LexicalToolbarElement extends HTMLElement {
|
|
149
237
|
static observedAttributes = [ "connected" ]
|
|
150
238
|
|
|
@@ -156,17 +244,14 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
156
244
|
|
|
157
245
|
connectedCallback() {
|
|
158
246
|
requestAnimationFrame(() => this.#refreshToolbarOverflow());
|
|
159
|
-
|
|
160
|
-
this
|
|
161
|
-
this._resizeObserver.observe(this);
|
|
247
|
+
this.setAttribute("role", "toolbar");
|
|
248
|
+
this.#installResizeObserver();
|
|
162
249
|
}
|
|
163
250
|
|
|
164
251
|
disconnectedCallback() {
|
|
165
|
-
|
|
166
|
-
this._resizeObserver.disconnect();
|
|
167
|
-
this._resizeObserver = null;
|
|
168
|
-
}
|
|
252
|
+
this.#uninstallResizeObserver();
|
|
169
253
|
this.#unbindHotkeys();
|
|
254
|
+
this.#unbindFocusListeners();
|
|
170
255
|
}
|
|
171
256
|
|
|
172
257
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
@@ -180,11 +265,12 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
180
265
|
this.editor = editorElement.editor;
|
|
181
266
|
this.#bindButtons();
|
|
182
267
|
this.#bindHotkeys();
|
|
183
|
-
this.#
|
|
268
|
+
this.#resetTabIndexValues();
|
|
184
269
|
this.#setItemPositionValues();
|
|
185
270
|
this.#monitorSelectionChanges();
|
|
186
271
|
this.#monitorHistoryChanges();
|
|
187
272
|
this.#refreshToolbarOverflow();
|
|
273
|
+
this.#bindFocusListeners();
|
|
188
274
|
|
|
189
275
|
this.toggleAttribute("connected", true);
|
|
190
276
|
}
|
|
@@ -194,24 +280,39 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
194
280
|
this.connectedCallback();
|
|
195
281
|
}
|
|
196
282
|
|
|
283
|
+
#installResizeObserver() {
|
|
284
|
+
this.resizeObserver = new ResizeObserver(() => this.#refreshToolbarOverflow());
|
|
285
|
+
this.resizeObserver.observe(this);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
#uninstallResizeObserver() {
|
|
289
|
+
if (this.resizeObserver) {
|
|
290
|
+
this.resizeObserver.disconnect();
|
|
291
|
+
this.resizeObserver = null;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
197
295
|
#bindButtons() {
|
|
198
296
|
this.addEventListener("click", this.#handleButtonClicked.bind(this));
|
|
199
297
|
}
|
|
200
298
|
|
|
201
|
-
#handleButtonClicked(
|
|
202
|
-
this.#handleTargetClicked(
|
|
299
|
+
#handleButtonClicked(event) {
|
|
300
|
+
this.#handleTargetClicked(event, "[data-command]", this.#dispatchButtonCommand.bind(this));
|
|
203
301
|
}
|
|
204
302
|
|
|
205
|
-
#handleTargetClicked(
|
|
206
|
-
const button = target.closest(selector);
|
|
303
|
+
#handleTargetClicked(event, selector, callback) {
|
|
304
|
+
const button = event.target.closest(selector);
|
|
207
305
|
if (button) {
|
|
208
|
-
callback(button);
|
|
306
|
+
callback(event, button);
|
|
209
307
|
}
|
|
210
308
|
}
|
|
211
309
|
|
|
212
|
-
#dispatchButtonCommand(
|
|
213
|
-
const
|
|
214
|
-
|
|
310
|
+
#dispatchButtonCommand(event, { dataset: { command, payload } }) {
|
|
311
|
+
const isKeyboard = event instanceof PointerEvent && event.pointerId === -1;
|
|
312
|
+
|
|
313
|
+
this.editor.update(() => {
|
|
314
|
+
this.editor.dispatchCommand(command, payload);
|
|
315
|
+
}, { tag: isKeyboard ? SKIP_DOM_SELECTION_TAG : undefined } );
|
|
215
316
|
}
|
|
216
317
|
|
|
217
318
|
#bindHotkeys() {
|
|
@@ -246,9 +347,38 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
246
347
|
return [ ...modifiers, pressedKey ].join("+")
|
|
247
348
|
}
|
|
248
349
|
|
|
249
|
-
#
|
|
250
|
-
this
|
|
251
|
-
|
|
350
|
+
#bindFocusListeners() {
|
|
351
|
+
this.editorElement.addEventListener("lexxy:focus", this.#handleFocus);
|
|
352
|
+
this.editorElement.addEventListener("lexxy:blur", this.#handleFocusOut);
|
|
353
|
+
this.addEventListener("focusout", this.#handleFocusOut);
|
|
354
|
+
this.addEventListener("keydown", this.#handleKeydown);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
#unbindFocusListeners() {
|
|
358
|
+
this.editorElement.removeEventListener("lexxy:focus", this.#handleFocus);
|
|
359
|
+
this.editorElement.removeEventListener("lexxy:blur", this.#handleFocusOut);
|
|
360
|
+
this.removeEventListener("focusout", this.#handleFocusOut);
|
|
361
|
+
this.removeEventListener("keydown", this.#handleKeydown);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
#handleFocus = () => {
|
|
365
|
+
this.#resetTabIndexValues();
|
|
366
|
+
this.#focusableItems[0].tabIndex = 0;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
#handleFocusOut = () => {
|
|
370
|
+
if (!this.contains(document.activeElement)) {
|
|
371
|
+
this.#resetTabIndexValues();
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
#handleKeydown = (event) => {
|
|
376
|
+
handleRollingTabIndex(this.#focusableItems, event);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
#resetTabIndexValues() {
|
|
380
|
+
this.#focusableItems.forEach((button) => {
|
|
381
|
+
button.tabIndex = -1;
|
|
252
382
|
});
|
|
253
383
|
}
|
|
254
384
|
|
|
@@ -358,6 +488,7 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
358
488
|
|
|
359
489
|
const isOverflowing = this.#overflowMenu.children.length > 0;
|
|
360
490
|
this.toggleAttribute("overflowing", isOverflowing);
|
|
491
|
+
this.#overflowMenu.toggleAttribute("disabled", !isOverflowing);
|
|
361
492
|
}
|
|
362
493
|
|
|
363
494
|
#compactMenu() {
|
|
@@ -409,6 +540,10 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
409
540
|
return Array.from(this.querySelectorAll(":scope > button"))
|
|
410
541
|
}
|
|
411
542
|
|
|
543
|
+
get #focusableItems() {
|
|
544
|
+
return Array.from(this.querySelectorAll(":scope button, :scope > details > summary"))
|
|
545
|
+
}
|
|
546
|
+
|
|
412
547
|
get #toolbarItems() {
|
|
413
548
|
return Array.from(this.querySelectorAll(":scope > *:not(.lexxy-editor__toolbar-overflow)"))
|
|
414
549
|
}
|
|
@@ -1386,7 +1521,7 @@ class CommandDispatcher {
|
|
|
1386
1521
|
}
|
|
1387
1522
|
|
|
1388
1523
|
#registerKeyboardCommands() {
|
|
1389
|
-
this.editor.registerCommand(KEY_TAB_COMMAND, this.#
|
|
1524
|
+
this.editor.registerCommand(KEY_TAB_COMMAND, this.#handleTabKey.bind(this), COMMAND_PRIORITY_NORMAL);
|
|
1390
1525
|
}
|
|
1391
1526
|
|
|
1392
1527
|
#registerDragAndDropHandlers() {
|
|
@@ -1436,18 +1571,28 @@ class CommandDispatcher {
|
|
|
1436
1571
|
this.editor.focus();
|
|
1437
1572
|
}
|
|
1438
1573
|
|
|
1439
|
-
#
|
|
1574
|
+
#handleTabKey(event) {
|
|
1440
1575
|
if (this.selection.isInsideList) {
|
|
1441
|
-
event
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
} else {
|
|
1445
|
-
return this.editor.dispatchCommand(INDENT_CONTENT_COMMAND, undefined)
|
|
1446
|
-
}
|
|
1576
|
+
return this.#handleTabForList(event)
|
|
1577
|
+
} else if (this.selection.isInsideCodeBlock) {
|
|
1578
|
+
return this.#handleTabForCode()
|
|
1447
1579
|
}
|
|
1448
1580
|
return false
|
|
1449
1581
|
}
|
|
1450
1582
|
|
|
1583
|
+
#handleTabForList(event) {
|
|
1584
|
+
if (event.shiftKey && !this.selection.isIndentedList) return false
|
|
1585
|
+
|
|
1586
|
+
event.preventDefault();
|
|
1587
|
+
const command = event.shiftKey? OUTDENT_CONTENT_COMMAND : INDENT_CONTENT_COMMAND;
|
|
1588
|
+
return this.editor.dispatchCommand(command)
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
#handleTabForCode() {
|
|
1592
|
+
const selection = $getSelection();
|
|
1593
|
+
return $isRangeSelection(selection) && selection.isCollapsed()
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1451
1596
|
// Not using TOGGLE_LINK_COMMAND because it's not handled unless you use React/LinkPlugin
|
|
1452
1597
|
#toggleLink(url) {
|
|
1453
1598
|
this.editor.update(() => {
|
|
@@ -1624,6 +1769,29 @@ class Selection {
|
|
|
1624
1769
|
return getNearestListItemNode(anchorNode) !== null
|
|
1625
1770
|
}
|
|
1626
1771
|
|
|
1772
|
+
get isIndentedList() {
|
|
1773
|
+
const selection = $getSelection();
|
|
1774
|
+
if (!$isRangeSelection(selection)) return false
|
|
1775
|
+
|
|
1776
|
+
const nodes = selection.getNodes();
|
|
1777
|
+
for (const node of nodes) {
|
|
1778
|
+
const closestListNode = $getNearestNodeOfType(node, ListNode);
|
|
1779
|
+
if (closestListNode && $getListDepth(closestListNode) > 1) {
|
|
1780
|
+
return true
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
return false
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
get isInsideCodeBlock() {
|
|
1788
|
+
const selection = $getSelection();
|
|
1789
|
+
if (!$isRangeSelection(selection)) return false
|
|
1790
|
+
|
|
1791
|
+
const anchorNode = selection.anchor.getNode();
|
|
1792
|
+
return $getNearestNodeOfType(anchorNode, CodeNode) !== null
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1627
1795
|
get nodeAfterCursor() {
|
|
1628
1796
|
const { anchorNode, offset } = this.#getCollapsedSelectionData();
|
|
1629
1797
|
if (!anchorNode) return null
|
|
@@ -3971,8 +4139,6 @@ class ToolbarDropdown extends HTMLElement {
|
|
|
3971
4139
|
|
|
3972
4140
|
this.container.addEventListener("toggle", this.#handleToggle.bind(this));
|
|
3973
4141
|
this.container.addEventListener("keydown", this.#handleKeyDown.bind(this));
|
|
3974
|
-
|
|
3975
|
-
this.#setTabIndexValues();
|
|
3976
4142
|
}
|
|
3977
4143
|
|
|
3978
4144
|
disconnectedCallback() {
|
|
@@ -4000,14 +4166,14 @@ class ToolbarDropdown extends HTMLElement {
|
|
|
4000
4166
|
}
|
|
4001
4167
|
}
|
|
4002
4168
|
|
|
4003
|
-
#handleOpen(
|
|
4004
|
-
this.trigger = trigger;
|
|
4169
|
+
#handleOpen() {
|
|
4005
4170
|
this.#interactiveElements[0].focus();
|
|
4006
4171
|
this.#setupClickOutsideHandler();
|
|
4172
|
+
|
|
4173
|
+
this.#resetTabIndexValues();
|
|
4007
4174
|
}
|
|
4008
4175
|
|
|
4009
4176
|
#handleClose() {
|
|
4010
|
-
this.trigger = null;
|
|
4011
4177
|
this.#removeClickOutsideHandler();
|
|
4012
4178
|
this.editor.focus();
|
|
4013
4179
|
}
|
|
@@ -4037,16 +4203,20 @@ class ToolbarDropdown extends HTMLElement {
|
|
|
4037
4203
|
}
|
|
4038
4204
|
}
|
|
4039
4205
|
|
|
4040
|
-
async #
|
|
4206
|
+
async #resetTabIndexValues() {
|
|
4041
4207
|
await nextFrame();
|
|
4042
|
-
this.#
|
|
4043
|
-
element.setAttribute("tabindex", 0);
|
|
4208
|
+
this.#buttons.forEach((element, index) => {
|
|
4209
|
+
element.setAttribute("tabindex", index === 0 ? 0 : "-1");
|
|
4044
4210
|
});
|
|
4045
4211
|
}
|
|
4046
4212
|
|
|
4047
4213
|
get #interactiveElements() {
|
|
4048
4214
|
return Array.from(this.querySelectorAll("button, input"))
|
|
4049
4215
|
}
|
|
4216
|
+
|
|
4217
|
+
get #buttons() {
|
|
4218
|
+
return Array.from(this.querySelectorAll("button"))
|
|
4219
|
+
}
|
|
4050
4220
|
}
|
|
4051
4221
|
|
|
4052
4222
|
class LinkDropdown extends ToolbarDropdown {
|
|
@@ -4242,15 +4412,19 @@ class TableHandler extends HTMLElement {
|
|
|
4242
4412
|
return $getTableColumnIndexFromTableCellNode(currentCell)
|
|
4243
4413
|
}
|
|
4244
4414
|
|
|
4415
|
+
get #tableHandlerButtons() {
|
|
4416
|
+
return Array.from(this.buttonsContainer.querySelectorAll("button, details > summary"))
|
|
4417
|
+
}
|
|
4418
|
+
|
|
4245
4419
|
#registerKeyboardShortcuts() {
|
|
4246
|
-
this.unregisterKeyboardShortcuts = this.#editor.registerCommand(KEY_DOWN_COMMAND, this.#handleKeyDown
|
|
4420
|
+
this.unregisterKeyboardShortcuts = this.#editor.registerCommand(KEY_DOWN_COMMAND, this.#handleKeyDown, COMMAND_PRIORITY_HIGH);
|
|
4247
4421
|
}
|
|
4248
4422
|
|
|
4249
4423
|
#unregisterKeyboardShortcuts() {
|
|
4250
4424
|
this.unregisterKeyboardShortcuts();
|
|
4251
4425
|
}
|
|
4252
4426
|
|
|
4253
|
-
#handleKeyDown(event) {
|
|
4427
|
+
#handleKeyDown = (event) => {
|
|
4254
4428
|
if ((event.ctrlKey || event.metaKey) && event.shiftKey && event.key === "F10") {
|
|
4255
4429
|
const firstButton = this.buttonsContainer?.querySelector("button, [tabindex]:not([tabindex='-1'])");
|
|
4256
4430
|
this.#setFocusStateOnSelectedCell();
|
|
@@ -4268,6 +4442,14 @@ class TableHandler extends HTMLElement {
|
|
|
4268
4442
|
}
|
|
4269
4443
|
}
|
|
4270
4444
|
|
|
4445
|
+
#handleTableHandlerKeydown = (event) => {
|
|
4446
|
+
if (event.key === "Escape") {
|
|
4447
|
+
this.#editor.focus();
|
|
4448
|
+
} else {
|
|
4449
|
+
handleRollingTabIndex(this.#tableHandlerButtons, event);
|
|
4450
|
+
}
|
|
4451
|
+
}
|
|
4452
|
+
|
|
4271
4453
|
#setUpButtons() {
|
|
4272
4454
|
this.buttonsContainer = createElement("div", {
|
|
4273
4455
|
className: "lexxy-table-handle-buttons"
|
|
@@ -4278,6 +4460,7 @@ class TableHandler extends HTMLElement {
|
|
|
4278
4460
|
|
|
4279
4461
|
this.moreMenu = this.#createMoreMenu();
|
|
4280
4462
|
this.buttonsContainer.appendChild(this.moreMenu);
|
|
4463
|
+
this.buttonsContainer.addEventListener("keydown", this.#handleTableHandlerKeydown);
|
|
4281
4464
|
|
|
4282
4465
|
this.#editorElement.appendChild(this.buttonsContainer);
|
|
4283
4466
|
}
|
|
@@ -437,17 +437,6 @@
|
|
|
437
437
|
}
|
|
438
438
|
}
|
|
439
439
|
}
|
|
440
|
-
|
|
441
|
-
.lexxy-editor__table-edit {
|
|
442
|
-
display: flex;
|
|
443
|
-
flex-direction: column;
|
|
444
|
-
flex-wrap: wrap;
|
|
445
|
-
gap: 0;
|
|
446
|
-
|
|
447
|
-
button {
|
|
448
|
-
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
440
|
}
|
|
452
441
|
|
|
453
442
|
/* Table handle buttons
|
|
@@ -455,6 +444,7 @@
|
|
|
455
444
|
|
|
456
445
|
:where(.lexxy-table-handle-buttons) {
|
|
457
446
|
--button-size: 2.5lh;
|
|
447
|
+
|
|
458
448
|
color: var(--lexxy-color-ink-inverted);
|
|
459
449
|
display: none;
|
|
460
450
|
flex-direction: row;
|
|
@@ -491,8 +481,7 @@
|
|
|
491
481
|
min-inline-size: var(--button-size);
|
|
492
482
|
padding: 0;
|
|
493
483
|
|
|
494
|
-
&:hover
|
|
495
|
-
&:focus-visible {
|
|
484
|
+
&:hover {
|
|
496
485
|
background-color: var(--lexxy-color-ink-medium);
|
|
497
486
|
}
|
|
498
487
|
|
|
@@ -506,6 +495,16 @@
|
|
|
506
495
|
display: none;
|
|
507
496
|
}
|
|
508
497
|
}
|
|
498
|
+
|
|
499
|
+
button,
|
|
500
|
+
summary {
|
|
501
|
+
&:focus,
|
|
502
|
+
&:focus-visible {
|
|
503
|
+
background-color: var(--lexxy-color-ink-medium);
|
|
504
|
+
outline: var(--lexxy-focus-ring-size) solid var(--lexxy-focus-ring-color);
|
|
505
|
+
outline-offset: var(--lexxy-focus-ring-offset);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
509
508
|
}
|
|
510
509
|
|
|
511
510
|
.lexxy-table-control__more-menu {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@37signals/lexxy",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.26-beta",
|
|
4
4
|
"description": "Lexxy - A modern rich text editor for Rails.",
|
|
5
5
|
"module": "dist/lexxy.esm.js",
|
|
6
6
|
"type": "module",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"scripts": {
|
|
26
26
|
"build": "rollup -c",
|
|
27
27
|
"build:npm": "rollup -c rollup.config.npm.mjs",
|
|
28
|
-
"watch": "
|
|
28
|
+
"watch": "rollup -wc --watch.onEnd=\"rails restart\"",
|
|
29
29
|
"lint": "eslint",
|
|
30
30
|
"prerelease": "yarn build:npm",
|
|
31
31
|
"release": "yarn build:npm && yarn publish"
|