@37signals/lexxy 0.7.3-beta → 0.7.4-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 +1213 -1161
- package/package.json +1 -1
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 {
|
|
14
|
-
import { $
|
|
13
|
+
import { HISTORY_MERGE_TAG, SKIP_DOM_SELECTION_TAG, SKIP_SCROLL_INTO_VIEW_TAG, $isTextNode, TextNode, $isRangeSelection, $getSelection, DecoratorNode, $getEditor, FORMAT_TEXT_COMMAND, $createTextNode, $isRootOrShadowRoot, 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, $getNodeByKey, $createNodeSelection, $setSelection, $createParagraphNode, KEY_ENTER_COMMAND, COMMAND_PRIORITY_HIGH, $isParagraphNode, $insertNodes, $createLineBreakNode, PASTE_TAG, createCommand, createState, defineExtension, $setState, $getState, $hasUpdateTag, CLEAR_HISTORY_COMMAND, $addUpdateTag, KEY_SPACE_COMMAND, KEY_DOWN_COMMAND } from 'lexical';
|
|
14
|
+
import { $isListItemNode, $isListNode, INSERT_UNORDERED_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND, ListNode, $getListDepth, $createListNode, ListItemNode, registerList } from '@lexical/list';
|
|
15
15
|
import { $isQuoteNode, $isHeadingNode, $createQuoteNode, $createHeadingNode, RichTextExtension, 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';
|
|
@@ -156,6 +156,8 @@ function getNonce() {
|
|
|
156
156
|
return element?.content
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
const SILENT_UPDATE_TAGS = [ HISTORY_MERGE_TAG, SKIP_DOM_SELECTION_TAG, SKIP_SCROLL_INTO_VIEW_TAG ];
|
|
160
|
+
|
|
159
161
|
function getNearestListItemNode(node) {
|
|
160
162
|
let current = node;
|
|
161
163
|
while (current !== null) {
|
|
@@ -465,7 +467,7 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
465
467
|
|
|
466
468
|
this.editor.update(() => {
|
|
467
469
|
this.editor.dispatchCommand(command, payload);
|
|
468
|
-
}, { tag: isKeyboard ? SKIP_DOM_SELECTION_TAG : undefined }
|
|
470
|
+
}, { tag: isKeyboard ? SKIP_DOM_SELECTION_TAG : undefined });
|
|
469
471
|
}
|
|
470
472
|
|
|
471
473
|
#bindHotkeys() {
|
|
@@ -532,13 +534,12 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
532
534
|
}
|
|
533
535
|
|
|
534
536
|
#monitorSelectionChanges() {
|
|
535
|
-
this.editor.
|
|
536
|
-
|
|
537
|
-
() => {
|
|
538
|
-
this.#closeDropdowns();
|
|
537
|
+
this.editor.registerUpdateListener(() => {
|
|
538
|
+
this.editor.getEditorState().read(() => {
|
|
539
539
|
this.#updateButtonStates();
|
|
540
|
-
|
|
541
|
-
}
|
|
540
|
+
this.#closeDropdowns();
|
|
541
|
+
});
|
|
542
|
+
});
|
|
542
543
|
}
|
|
543
544
|
|
|
544
545
|
#monitorHistoryChanges() {
|
|
@@ -802,8 +803,6 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
802
803
|
}
|
|
803
804
|
}
|
|
804
805
|
|
|
805
|
-
customElements.define("lexxy-toolbar", LexicalToolbarElement);
|
|
806
|
-
|
|
807
806
|
var theme = {
|
|
808
807
|
text: {
|
|
809
808
|
bold: "lexxy-content__bold",
|
|
@@ -969,6 +968,8 @@ class ActionTextAttachmentNode extends DecoratorNode {
|
|
|
969
968
|
this.fileSize = fileSize;
|
|
970
969
|
this.width = width;
|
|
971
970
|
this.height = height;
|
|
971
|
+
|
|
972
|
+
this.editor = $getEditor();
|
|
972
973
|
}
|
|
973
974
|
|
|
974
975
|
createDOM() {
|
|
@@ -989,8 +990,13 @@ class ActionTextAttachmentNode extends DecoratorNode {
|
|
|
989
990
|
return figure
|
|
990
991
|
}
|
|
991
992
|
|
|
992
|
-
updateDOM() {
|
|
993
|
-
|
|
993
|
+
updateDOM(_prevNode, dom) {
|
|
994
|
+
const caption = dom.querySelector("figcaption textarea");
|
|
995
|
+
if (caption && this.caption) {
|
|
996
|
+
caption.value = this.caption;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
return false
|
|
994
1000
|
}
|
|
995
1001
|
|
|
996
1002
|
getTextContent() {
|
|
@@ -1098,8 +1104,8 @@ class ActionTextAttachmentNode extends DecoratorNode {
|
|
|
1098
1104
|
});
|
|
1099
1105
|
|
|
1100
1106
|
input.addEventListener("focusin", () => input.placeholder = "Add caption...");
|
|
1101
|
-
input.addEventListener("blur", this.#handleCaptionInputBlurred
|
|
1102
|
-
input.addEventListener("keydown", this.#handleCaptionInputKeydown
|
|
1107
|
+
input.addEventListener("blur", (event) => this.#handleCaptionInputBlurred(event));
|
|
1108
|
+
input.addEventListener("keydown", (event) => this.#handleCaptionInputKeydown(event));
|
|
1103
1109
|
|
|
1104
1110
|
caption.appendChild(input);
|
|
1105
1111
|
|
|
@@ -1107,21 +1113,24 @@ class ActionTextAttachmentNode extends DecoratorNode {
|
|
|
1107
1113
|
}
|
|
1108
1114
|
|
|
1109
1115
|
#handleCaptionInputBlurred(event) {
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
input.placeholder = this.fileName;
|
|
1113
|
-
this.#updateCaptionValueFromInput(input);
|
|
1116
|
+
this.#updateCaptionValueFromInput(event.target);
|
|
1114
1117
|
}
|
|
1115
1118
|
|
|
1116
1119
|
#updateCaptionValueFromInput(input) {
|
|
1117
|
-
|
|
1120
|
+
input.placeholder = this.fileName;
|
|
1121
|
+
this.editor.update(() => {
|
|
1122
|
+
this.getWritable().caption = input.value;
|
|
1123
|
+
});
|
|
1118
1124
|
}
|
|
1119
1125
|
|
|
1120
1126
|
#handleCaptionInputKeydown(event) {
|
|
1121
1127
|
if (event.key === "Enter") {
|
|
1122
1128
|
this.#updateCaptionValueFromInput(event.target);
|
|
1123
|
-
dispatchCustomEvent(event.target, "lexxy:internal:move-to-next-line");
|
|
1124
1129
|
event.preventDefault();
|
|
1130
|
+
|
|
1131
|
+
this.editor.update(() => {
|
|
1132
|
+
this.selectNext();
|
|
1133
|
+
}, { tag: HISTORY_MERGE_TAG });
|
|
1125
1134
|
}
|
|
1126
1135
|
event.stopPropagation();
|
|
1127
1136
|
}
|
|
@@ -1162,56 +1171,83 @@ class ActionTextAttachmentUploadNode extends ActionTextAttachmentNode {
|
|
|
1162
1171
|
}
|
|
1163
1172
|
|
|
1164
1173
|
constructor(node, key) {
|
|
1165
|
-
const { file, uploadUrl, blobUrlTemplate,
|
|
1174
|
+
const { file, uploadUrl, blobUrlTemplate, progress, width, height, uploadError } = node;
|
|
1166
1175
|
super({ ...node, contentType: file.type }, key);
|
|
1167
1176
|
this.file = file;
|
|
1168
1177
|
this.uploadUrl = uploadUrl;
|
|
1169
1178
|
this.blobUrlTemplate = blobUrlTemplate;
|
|
1170
|
-
this.
|
|
1171
|
-
this.
|
|
1172
|
-
this.
|
|
1179
|
+
this.progress = progress ?? null;
|
|
1180
|
+
this.width = width;
|
|
1181
|
+
this.height = height;
|
|
1182
|
+
this.uploadError = uploadError;
|
|
1173
1183
|
}
|
|
1174
1184
|
|
|
1175
1185
|
createDOM() {
|
|
1186
|
+
if (this.uploadError) return this.#createDOMForError()
|
|
1187
|
+
|
|
1188
|
+
// This side-effect is trigged on DOM load to fire only once and avoid multiple
|
|
1189
|
+
// uploads through cloning. The upload is guarded from restarting in case the
|
|
1190
|
+
// node is reloaded from saved state such as from history.
|
|
1191
|
+
this.#startUploadIfNeeded();
|
|
1192
|
+
|
|
1176
1193
|
const figure = this.createAttachmentFigure();
|
|
1177
1194
|
|
|
1178
1195
|
if (this.isPreviewableAttachment) {
|
|
1179
|
-
figure.appendChild(this.#createDOMForImage());
|
|
1196
|
+
const img = figure.appendChild(this.#createDOMForImage());
|
|
1197
|
+
|
|
1198
|
+
// load file locally to set dimensions and prevent vertical shifting
|
|
1199
|
+
loadFileIntoImage(this.file, img).then(img => this.#setDimensionsFromImage(img));
|
|
1180
1200
|
} else {
|
|
1181
1201
|
figure.appendChild(this.#createDOMForFile());
|
|
1182
1202
|
}
|
|
1183
1203
|
|
|
1184
1204
|
figure.appendChild(this.#createCaption());
|
|
1205
|
+
figure.appendChild(this.#createProgressBar());
|
|
1185
1206
|
|
|
1186
|
-
|
|
1187
|
-
|
|
1207
|
+
return figure
|
|
1208
|
+
}
|
|
1188
1209
|
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
this.#loadFigure(figure).then(() => this.#startUpload(progressBar, figure));
|
|
1210
|
+
updateDOM(prevNode, dom) {
|
|
1211
|
+
if (this.uploadError !== prevNode.uploadError) return true
|
|
1192
1212
|
|
|
1193
|
-
|
|
1213
|
+
if (prevNode.progress !== this.progress) {
|
|
1214
|
+
const progress = dom.querySelector("progress");
|
|
1215
|
+
progress.value = this.progress ?? 0;
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
return false
|
|
1194
1219
|
}
|
|
1195
1220
|
|
|
1196
1221
|
exportDOM() {
|
|
1197
1222
|
const img = document.createElement("img");
|
|
1198
|
-
if (this.src) {
|
|
1199
|
-
img.src = this.src;
|
|
1200
|
-
}
|
|
1201
1223
|
return { element: img }
|
|
1202
1224
|
}
|
|
1203
1225
|
|
|
1204
1226
|
exportJSON() {
|
|
1205
1227
|
return {
|
|
1228
|
+
...super.exportJSON(),
|
|
1206
1229
|
type: "action_text_attachment_upload",
|
|
1207
1230
|
version: 1,
|
|
1208
|
-
progress: this.progress,
|
|
1209
1231
|
uploadUrl: this.uploadUrl,
|
|
1210
1232
|
blobUrlTemplate: this.blobUrlTemplate,
|
|
1211
|
-
|
|
1233
|
+
progress: this.progress,
|
|
1234
|
+
width: this.width,
|
|
1235
|
+
height: this.height,
|
|
1236
|
+
uploadError: this.uploadError
|
|
1212
1237
|
}
|
|
1213
1238
|
}
|
|
1214
1239
|
|
|
1240
|
+
get #uploadStarted() {
|
|
1241
|
+
return this.progress !== null
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
#createDOMForError() {
|
|
1245
|
+
const figure = this.createAttachmentFigure();
|
|
1246
|
+
figure.classList.add("attachment--error");
|
|
1247
|
+
figure.appendChild(createElement("div", { innerText: `Error uploading ${this.file?.name ?? "file"}` }));
|
|
1248
|
+
return figure
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1215
1251
|
#createDOMForImage() {
|
|
1216
1252
|
return createElement("img")
|
|
1217
1253
|
}
|
|
@@ -1237,94 +1273,126 @@ class ActionTextAttachmentUploadNode extends ActionTextAttachmentNode {
|
|
|
1237
1273
|
return figcaption
|
|
1238
1274
|
}
|
|
1239
1275
|
|
|
1240
|
-
#
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1276
|
+
#createProgressBar() {
|
|
1277
|
+
return createElement("progress", { value: this.progress ?? 0, max: 100 })
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
#setDimensionsFromImage({ width, height }) {
|
|
1281
|
+
if (this.#hasDimensions) return
|
|
1282
|
+
|
|
1283
|
+
this.editor.update(() => {
|
|
1284
|
+
const writable = this.getWritable();
|
|
1285
|
+
writable.width = width;
|
|
1286
|
+
writable.height = height;
|
|
1287
|
+
}, { tag: SILENT_UPDATE_TAGS });
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
get #hasDimensions() {
|
|
1291
|
+
return Boolean(this.width && this.height)
|
|
1247
1292
|
}
|
|
1248
1293
|
|
|
1249
|
-
async #
|
|
1294
|
+
async #startUploadIfNeeded() {
|
|
1295
|
+
if (this.#uploadStarted) return
|
|
1296
|
+
|
|
1297
|
+
this.#setUploadStarted();
|
|
1298
|
+
|
|
1250
1299
|
const { DirectUpload } = await import('@rails/activestorage');
|
|
1251
|
-
const shouldAuthenticateUploads = Lexxy.global.get("authenticatedUploads");
|
|
1252
1300
|
|
|
1253
1301
|
const upload = new DirectUpload(this.file, this.uploadUrl, this);
|
|
1302
|
+
upload.delegate = this.#createUploadDelegate();
|
|
1303
|
+
upload.create((error, blob) => {
|
|
1304
|
+
if (error) {
|
|
1305
|
+
this.#handleUploadError(error);
|
|
1306
|
+
} else {
|
|
1307
|
+
this.#showUploadedAttachment(blob);
|
|
1308
|
+
}
|
|
1309
|
+
});
|
|
1310
|
+
}
|
|
1254
1311
|
|
|
1255
|
-
|
|
1312
|
+
#createUploadDelegate() {
|
|
1313
|
+
const shouldAuthenticateUploads = Lexxy.global.get("authenticatedUploads");
|
|
1314
|
+
|
|
1315
|
+
return {
|
|
1256
1316
|
directUploadWillCreateBlobWithXHR: (request) => {
|
|
1257
1317
|
if (shouldAuthenticateUploads) request.withCredentials = true;
|
|
1258
1318
|
},
|
|
1259
1319
|
directUploadWillStoreFileWithXHR: (request) => {
|
|
1260
1320
|
if (shouldAuthenticateUploads) request.withCredentials = true;
|
|
1261
1321
|
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
progressBar.value = Math.round(event.loaded / event.total * 100);
|
|
1265
|
-
});
|
|
1266
|
-
});
|
|
1322
|
+
const uploadProgressHandler = (event) => this.#handleUploadProgress(event);
|
|
1323
|
+
request.upload.addEventListener("progress", uploadProgressHandler);
|
|
1267
1324
|
}
|
|
1268
|
-
}
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1269
1327
|
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
this.#handleUploadError(figure);
|
|
1273
|
-
} else {
|
|
1274
|
-
this.#loadFigurePreviewFromBlob(blob, figure).then(() => {
|
|
1275
|
-
this.#showUploadedAttachment(figure, blob);
|
|
1276
|
-
});
|
|
1277
|
-
}
|
|
1278
|
-
});
|
|
1328
|
+
#setUploadStarted() {
|
|
1329
|
+
this.#setProgress(1);
|
|
1279
1330
|
}
|
|
1280
1331
|
|
|
1281
|
-
#
|
|
1282
|
-
|
|
1283
|
-
figure.classList.add("attachment--error");
|
|
1284
|
-
figure.appendChild(createElement("div", { innerText: `Error uploading ${this.file?.name ?? "image"}` }));
|
|
1332
|
+
#handleUploadProgress(event) {
|
|
1333
|
+
this.#setProgress(Math.round(event.loaded / event.total * 100));
|
|
1285
1334
|
}
|
|
1286
1335
|
|
|
1287
|
-
|
|
1336
|
+
#setProgress(progress) {
|
|
1288
1337
|
this.editor.update(() => {
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
const src = this.blobUrlTemplate
|
|
1292
|
-
.replace(":signed_id", blob.signed_id)
|
|
1293
|
-
.replace(":filename", encodeURIComponent(blob.filename));
|
|
1294
|
-
const latest = $getNodeByKey(this.getKey());
|
|
1295
|
-
if (latest) {
|
|
1296
|
-
latest.replace(new ActionTextAttachmentNode({
|
|
1297
|
-
tagName: this.tagName,
|
|
1298
|
-
sgid: blob.attachable_sgid,
|
|
1299
|
-
src: blob.previewable ? blob.url : src,
|
|
1300
|
-
altText: blob.filename,
|
|
1301
|
-
contentType: blob.content_type,
|
|
1302
|
-
fileName: blob.filename,
|
|
1303
|
-
fileSize: blob.byte_size,
|
|
1304
|
-
width: image?.naturalWidth,
|
|
1305
|
-
previewable: blob.previewable,
|
|
1306
|
-
height: image?.naturalHeight
|
|
1307
|
-
}));
|
|
1308
|
-
}
|
|
1309
|
-
}, { tag: HISTORY_MERGE_TAG });
|
|
1338
|
+
this.getWritable().progress = progress;
|
|
1339
|
+
}, { tag: SILENT_UPDATE_TAGS });
|
|
1310
1340
|
}
|
|
1311
1341
|
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1342
|
+
#handleUploadError(error) {
|
|
1343
|
+
console.warn(`Upload error for ${this.file?.name ?? "file"}: ${error}`);
|
|
1344
|
+
this.editor.update(() => {
|
|
1345
|
+
this.getWritable().uploadError = true;
|
|
1346
|
+
}, { tag: SILENT_UPDATE_TAGS });
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
async #showUploadedAttachment(blob) {
|
|
1350
|
+
this.editor.update(() => {
|
|
1351
|
+
this.replace(this.#toActionTextAttachmentNodeWith(blob));
|
|
1352
|
+
}, { tag: SILENT_UPDATE_TAGS });
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
#toActionTextAttachmentNodeWith(blob) {
|
|
1356
|
+
const conversion = new AttachmentNodeConversion(this, blob);
|
|
1357
|
+
return conversion.toAttachmentNode()
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
class AttachmentNodeConversion {
|
|
1362
|
+
constructor(uploadNode, blob) {
|
|
1363
|
+
this.uploadNode = uploadNode;
|
|
1364
|
+
this.blob = blob;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
toAttachmentNode() {
|
|
1368
|
+
return new ActionTextAttachmentNode({
|
|
1369
|
+
...this.uploadNode,
|
|
1370
|
+
...this.#propertiesFromBlob,
|
|
1371
|
+
src: this.#src
|
|
1372
|
+
})
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
get #propertiesFromBlob() {
|
|
1376
|
+
const { blob } = this;
|
|
1377
|
+
return {
|
|
1378
|
+
sgid: blob.attachable_sgid,
|
|
1379
|
+
altText: blob.filename,
|
|
1380
|
+
contentType: blob.content_type,
|
|
1381
|
+
fileName: blob.filename,
|
|
1382
|
+
fileSize: blob.byte_size,
|
|
1383
|
+
previewable: blob.previewable,
|
|
1326
1384
|
}
|
|
1327
1385
|
}
|
|
1386
|
+
|
|
1387
|
+
get #src() {
|
|
1388
|
+
return this.blob.previewable ? this.blob.url : this.#blobSrc
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
get #blobSrc() {
|
|
1392
|
+
return this.uploadNode.blobUrlTemplate
|
|
1393
|
+
.replace(":signed_id", this.blob.signed_id)
|
|
1394
|
+
.replace(":filename", encodeURIComponent(this.blob.filename))
|
|
1395
|
+
}
|
|
1328
1396
|
}
|
|
1329
1397
|
|
|
1330
1398
|
class HorizontalDividerNode extends DecoratorNode {
|
|
@@ -3114,7 +3182,7 @@ class Contents {
|
|
|
3114
3182
|
const blobUrlTemplate = this.editorElement.blobUrlTemplate;
|
|
3115
3183
|
|
|
3116
3184
|
this.editor.update(() => {
|
|
3117
|
-
const uploadedImageNode = new ActionTextAttachmentUploadNode({ file: file, uploadUrl: uploadUrl, blobUrlTemplate: blobUrlTemplate
|
|
3185
|
+
const uploadedImageNode = new ActionTextAttachmentUploadNode({ file: file, uploadUrl: uploadUrl, blobUrlTemplate: blobUrlTemplate });
|
|
3118
3186
|
this.insertAtCursor(uploadedImageNode);
|
|
3119
3187
|
}, { tag: HISTORY_MERGE_TAG });
|
|
3120
3188
|
}
|
|
@@ -3731,7 +3799,7 @@ class Extensions {
|
|
|
3731
3799
|
|
|
3732
3800
|
initializeToolbars() {
|
|
3733
3801
|
if (this.#lexxyToolbar) {
|
|
3734
|
-
this.enabledExtensions.forEach(ext => ext.
|
|
3802
|
+
this.enabledExtensions.forEach(ext => ext.initializeToolbar(this.#lexxyToolbar));
|
|
3735
3803
|
}
|
|
3736
3804
|
}
|
|
3737
3805
|
|
|
@@ -4225,7 +4293,6 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
4225
4293
|
#initialize() {
|
|
4226
4294
|
this.#synchronizeWithChanges();
|
|
4227
4295
|
this.#registerComponents();
|
|
4228
|
-
this.#listenForInvalidatedNodes();
|
|
4229
4296
|
this.#handleEnter();
|
|
4230
4297
|
this.#registerFocusEvents();
|
|
4231
4298
|
this.#attachDebugHooks();
|
|
@@ -4238,11 +4305,11 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
4238
4305
|
this.editorContentElement ||= this.#createEditorContentElement();
|
|
4239
4306
|
|
|
4240
4307
|
const editor = buildEditorFromExtensions({
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4308
|
+
name: "lexxy/core",
|
|
4309
|
+
namespace: "Lexxy",
|
|
4310
|
+
theme: theme,
|
|
4311
|
+
nodes: this.#lexicalNodes
|
|
4312
|
+
},
|
|
4246
4313
|
...this.#lexicalExtensions
|
|
4247
4314
|
);
|
|
4248
4315
|
|
|
@@ -4252,7 +4319,7 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
4252
4319
|
}
|
|
4253
4320
|
|
|
4254
4321
|
get #lexicalExtensions() {
|
|
4255
|
-
const extensions = [
|
|
4322
|
+
const extensions = [];
|
|
4256
4323
|
const richTextExtensions = [
|
|
4257
4324
|
this.highlighter.lexicalExtension,
|
|
4258
4325
|
TrixContentExtension,
|
|
@@ -4405,21 +4472,6 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
4405
4472
|
this.append(this.codeLanguagePicker);
|
|
4406
4473
|
}
|
|
4407
4474
|
|
|
4408
|
-
#listenForInvalidatedNodes() {
|
|
4409
|
-
this.editor.getRootElement().addEventListener("lexxy:internal:invalidate-node", (event) => {
|
|
4410
|
-
const { key, values } = event.detail;
|
|
4411
|
-
|
|
4412
|
-
this.editor.update(() => {
|
|
4413
|
-
const node = $getNodeByKey(key);
|
|
4414
|
-
|
|
4415
|
-
if (node instanceof ActionTextAttachmentNode) {
|
|
4416
|
-
const updatedNode = node.getWritable();
|
|
4417
|
-
Object.assign(updatedNode, values);
|
|
4418
|
-
}
|
|
4419
|
-
});
|
|
4420
|
-
});
|
|
4421
|
-
}
|
|
4422
|
-
|
|
4423
4475
|
#handleEnter() {
|
|
4424
4476
|
// We can't prevent these externally using regular keydown because Lexical handles it first.
|
|
4425
4477
|
this.editor.registerCommand(
|
|
@@ -4482,14 +4534,7 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
4482
4534
|
|
|
4483
4535
|
|
|
4484
4536
|
#attachDebugHooks() {
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
this.#addUnregisterHandler(this.editor.registerUpdateListener(({ editorState }) => {
|
|
4488
|
-
editorState.read(() => {
|
|
4489
|
-
console.debug("HTML: ", this.value, "String:", this.toString());
|
|
4490
|
-
console.debug("empty", this.isEmpty, "blank", this.isBlank);
|
|
4491
|
-
});
|
|
4492
|
-
}));
|
|
4537
|
+
return
|
|
4493
4538
|
}
|
|
4494
4539
|
|
|
4495
4540
|
#attachToolbar() {
|
|
@@ -4569,8 +4614,6 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
4569
4614
|
}
|
|
4570
4615
|
}
|
|
4571
4616
|
|
|
4572
|
-
customElements.define("lexxy-editor", LexicalEditorElement);
|
|
4573
|
-
|
|
4574
4617
|
class ToolbarDropdown extends HTMLElement {
|
|
4575
4618
|
connectedCallback() {
|
|
4576
4619
|
this.container = this.closest("details");
|
|
@@ -4696,8 +4739,6 @@ class LinkDropdown extends ToolbarDropdown {
|
|
|
4696
4739
|
}
|
|
4697
4740
|
}
|
|
4698
4741
|
|
|
4699
|
-
customElements.define("lexxy-link-dropdown", LinkDropdown);
|
|
4700
|
-
|
|
4701
4742
|
const APPLY_HIGHLIGHT_SELECTOR = "button.lexxy-highlight-button";
|
|
4702
4743
|
const REMOVE_HIGHLIGHT_SELECTOR = "[data-command='removeHighlight']";
|
|
4703
4744
|
|
|
@@ -4805,1425 +4846,1433 @@ class HighlightDropdown extends ToolbarDropdown {
|
|
|
4805
4846
|
}
|
|
4806
4847
|
}
|
|
4807
4848
|
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
this.contents = editorElement.contents;
|
|
4814
|
-
this.selection = editorElement.selection;
|
|
4815
|
-
|
|
4816
|
-
this.currentTableNodeKey = null;
|
|
4817
|
-
this.currentCellKey = null;
|
|
4849
|
+
class BaseSource {
|
|
4850
|
+
// Template method to override
|
|
4851
|
+
async buildListItems(filter = "") {
|
|
4852
|
+
return Promise.resolve([])
|
|
4853
|
+
}
|
|
4818
4854
|
|
|
4819
|
-
|
|
4855
|
+
// Template method to override
|
|
4856
|
+
promptItemFor(listItem) {
|
|
4857
|
+
return null
|
|
4820
4858
|
}
|
|
4821
4859
|
|
|
4822
|
-
|
|
4823
|
-
this.currentTableNodeKey = null;
|
|
4824
|
-
this.currentCellKey = null;
|
|
4860
|
+
// Protected
|
|
4825
4861
|
|
|
4826
|
-
|
|
4862
|
+
buildListItemElementFor(promptItemElement) {
|
|
4863
|
+
const template = promptItemElement.querySelector("template[type='menu']");
|
|
4864
|
+
const fragment = template.content.cloneNode(true);
|
|
4865
|
+
const listItemElement = createElement("li", { role: "option", id: generateDomId("prompt-item"), tabindex: "0" });
|
|
4866
|
+
listItemElement.classList.add("lexxy-prompt-menu__item");
|
|
4867
|
+
listItemElement.appendChild(fragment);
|
|
4868
|
+
return listItemElement
|
|
4827
4869
|
}
|
|
4828
4870
|
|
|
4829
|
-
|
|
4830
|
-
|
|
4871
|
+
async loadPromptItemsFromUrl(url) {
|
|
4872
|
+
try {
|
|
4873
|
+
const response = await fetch(url);
|
|
4874
|
+
const html = await response.text();
|
|
4875
|
+
const promptItems = parseHtml(html).querySelectorAll("lexxy-prompt-item");
|
|
4876
|
+
return Promise.resolve(Array.from(promptItems))
|
|
4877
|
+
} catch (error) {
|
|
4878
|
+
return Promise.reject(error)
|
|
4879
|
+
}
|
|
4880
|
+
}
|
|
4881
|
+
}
|
|
4831
4882
|
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4883
|
+
class LocalFilterSource extends BaseSource {
|
|
4884
|
+
async buildListItems(filter = "") {
|
|
4885
|
+
const promptItems = await this.fetchPromptItems();
|
|
4886
|
+
return this.#buildListItemsFromPromptItems(promptItems, filter)
|
|
4836
4887
|
}
|
|
4837
4888
|
|
|
4838
|
-
|
|
4839
|
-
|
|
4889
|
+
// Template method to override
|
|
4890
|
+
async fetchPromptItems(filter) {
|
|
4891
|
+
return Promise.resolve([])
|
|
4892
|
+
}
|
|
4840
4893
|
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
return (tableNode instanceof TableNode) ? tableNode : null
|
|
4844
|
-
})
|
|
4894
|
+
promptItemFor(listItem) {
|
|
4895
|
+
return this.promptItemByListItem.get(listItem)
|
|
4845
4896
|
}
|
|
4846
4897
|
|
|
4847
|
-
|
|
4848
|
-
const
|
|
4898
|
+
#buildListItemsFromPromptItems(promptItems, filter) {
|
|
4899
|
+
const listItems = [];
|
|
4900
|
+
this.promptItemByListItem = new WeakMap();
|
|
4901
|
+
promptItems.forEach((promptItem) => {
|
|
4902
|
+
const searchableText = promptItem.getAttribute("search");
|
|
4849
4903
|
|
|
4850
|
-
|
|
4851
|
-
|
|
4904
|
+
if (!filter || filterMatches(searchableText, filter)) {
|
|
4905
|
+
const listItem = this.buildListItemElementFor(promptItem);
|
|
4906
|
+
this.promptItemByListItem.set(listItem, promptItem);
|
|
4907
|
+
listItems.push(listItem);
|
|
4908
|
+
}
|
|
4909
|
+
});
|
|
4852
4910
|
|
|
4853
|
-
return
|
|
4854
|
-
return rows[currentRowIndex]?.getChildren() ?? null
|
|
4855
|
-
}) ?? null
|
|
4911
|
+
return listItems
|
|
4856
4912
|
}
|
|
4913
|
+
}
|
|
4857
4914
|
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4915
|
+
class InlinePromptSource extends LocalFilterSource {
|
|
4916
|
+
constructor(inlinePromptItems) {
|
|
4917
|
+
super();
|
|
4918
|
+
this.inlinePromptItemElements = Array.from(inlinePromptItems);
|
|
4919
|
+
}
|
|
4861
4920
|
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
}) ?? 0
|
|
4921
|
+
async fetchPromptItems() {
|
|
4922
|
+
return Promise.resolve(this.inlinePromptItemElements)
|
|
4865
4923
|
}
|
|
4924
|
+
}
|
|
4866
4925
|
|
|
4867
|
-
|
|
4868
|
-
|
|
4926
|
+
class DeferredPromptSource extends LocalFilterSource {
|
|
4927
|
+
constructor(url) {
|
|
4928
|
+
super();
|
|
4929
|
+
this.url = url;
|
|
4869
4930
|
|
|
4870
|
-
|
|
4871
|
-
|
|
4931
|
+
this.fetchPromptItems();
|
|
4932
|
+
}
|
|
4872
4933
|
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4934
|
+
async fetchPromptItems() {
|
|
4935
|
+
this.promptItems ??= await this.loadPromptItemsFromUrl(this.url);
|
|
4936
|
+
|
|
4937
|
+
return Promise.resolve(this.promptItems)
|
|
4876
4938
|
}
|
|
4939
|
+
}
|
|
4877
4940
|
|
|
4878
|
-
|
|
4879
|
-
const currentCell = this.currentCell;
|
|
4880
|
-
if (!currentCell) return 0
|
|
4941
|
+
const DEBOUNCE_INTERVAL = 200;
|
|
4881
4942
|
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4943
|
+
class RemoteFilterSource extends BaseSource {
|
|
4944
|
+
constructor(url) {
|
|
4945
|
+
super();
|
|
4946
|
+
|
|
4947
|
+
this.baseURL = url;
|
|
4948
|
+
this.loadAndFilterListItems = debounceAsync(this.fetchFilteredListItems.bind(this), DEBOUNCE_INTERVAL);
|
|
4885
4949
|
}
|
|
4886
4950
|
|
|
4887
|
-
|
|
4888
|
-
return this.
|
|
4889
|
-
return this.currentTableNode?.getChildren()
|
|
4890
|
-
}) ?? null
|
|
4951
|
+
async buildListItems(filter = "") {
|
|
4952
|
+
return await this.loadAndFilterListItems(filter)
|
|
4891
4953
|
}
|
|
4892
4954
|
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
this.editor.getEditorState().read(() => {
|
|
4898
|
-
const selection = $getSelection();
|
|
4899
|
-
if (!selection || !this.selection.isTableCellSelected) return
|
|
4900
|
-
|
|
4901
|
-
const node = selection.getNodes()[0];
|
|
4955
|
+
promptItemFor(listItem) {
|
|
4956
|
+
return this.promptItemByListItem.get(listItem)
|
|
4957
|
+
}
|
|
4902
4958
|
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4959
|
+
async fetchFilteredListItems(filter) {
|
|
4960
|
+
const promptItems = await this.loadPromptItemsFromUrl(this.#urlFor(filter));
|
|
4961
|
+
return this.#buildListItemsFromPromptItems(promptItems)
|
|
4962
|
+
}
|
|
4906
4963
|
|
|
4907
|
-
|
|
4908
|
-
|
|
4964
|
+
#urlFor(filter) {
|
|
4965
|
+
const url = new URL(this.baseURL, window.location.origin);
|
|
4966
|
+
url.searchParams.append("filter", filter);
|
|
4967
|
+
return url.toString()
|
|
4909
4968
|
}
|
|
4910
4969
|
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
return
|
|
4915
|
-
}
|
|
4970
|
+
#buildListItemsFromPromptItems(promptItems) {
|
|
4971
|
+
const listItems = [];
|
|
4972
|
+
this.promptItemByListItem = new WeakMap();
|
|
4916
4973
|
|
|
4917
|
-
|
|
4918
|
-
this
|
|
4919
|
-
|
|
4974
|
+
for (const promptItem of promptItems) {
|
|
4975
|
+
const listItem = this.buildListItemElementFor(promptItem);
|
|
4976
|
+
this.promptItemByListItem.set(listItem, promptItem);
|
|
4977
|
+
listItems.push(listItem);
|
|
4920
4978
|
}
|
|
4921
4979
|
|
|
4922
|
-
|
|
4980
|
+
return listItems
|
|
4923
4981
|
}
|
|
4982
|
+
}
|
|
4924
4983
|
|
|
4925
|
-
|
|
4926
|
-
this.#selectCellAtSelection();
|
|
4927
|
-
this.editor.dispatchCommand(this.#commandName(command));
|
|
4928
|
-
this.#selectNextBestCell(command, customIndex);
|
|
4929
|
-
}
|
|
4984
|
+
const NOTHING_FOUND_DEFAULT_MESSAGE = "Nothing found";
|
|
4930
4985
|
|
|
4931
|
-
|
|
4932
|
-
|
|
4986
|
+
class LexicalPromptElement extends HTMLElement {
|
|
4987
|
+
constructor() {
|
|
4988
|
+
super();
|
|
4989
|
+
this.keyListeners = [];
|
|
4990
|
+
}
|
|
4933
4991
|
|
|
4934
|
-
|
|
4935
|
-
let headerState = null;
|
|
4992
|
+
static observedAttributes = [ "connected" ]
|
|
4936
4993
|
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
headerState = TableCellHeaderStates.ROW;
|
|
4940
|
-
} else if (childType === "column") {
|
|
4941
|
-
cells = this.currentColumnCells;
|
|
4942
|
-
headerState = TableCellHeaderStates.COLUMN;
|
|
4943
|
-
}
|
|
4994
|
+
connectedCallback() {
|
|
4995
|
+
this.source = this.#createSource();
|
|
4944
4996
|
|
|
4945
|
-
|
|
4997
|
+
this.#addTriggerListener();
|
|
4998
|
+
this.toggleAttribute("connected", true);
|
|
4999
|
+
}
|
|
4946
5000
|
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
5001
|
+
disconnectedCallback() {
|
|
5002
|
+
this.source = null;
|
|
5003
|
+
this.popoverElement = null;
|
|
5004
|
+
}
|
|
4950
5005
|
|
|
4951
|
-
const currentStyle = firstCell.getHeaderStyles();
|
|
4952
|
-
const newStyle = currentStyle ^ headerState;
|
|
4953
5006
|
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
}
|
|
5007
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
5008
|
+
if (name === "connected" && this.isConnected && oldValue != null && oldValue !== newValue) {
|
|
5009
|
+
requestAnimationFrame(() => this.#reconnect());
|
|
5010
|
+
}
|
|
4958
5011
|
}
|
|
4959
5012
|
|
|
4960
|
-
|
|
4961
|
-
this
|
|
4962
|
-
this.editor.dispatchCommand("deleteTable");
|
|
5013
|
+
get name() {
|
|
5014
|
+
return this.getAttribute("name")
|
|
4963
5015
|
}
|
|
4964
5016
|
|
|
4965
|
-
|
|
4966
|
-
this.
|
|
4967
|
-
|
|
4968
|
-
if (!selection) return
|
|
5017
|
+
get trigger() {
|
|
5018
|
+
return this.getAttribute("trigger")
|
|
5019
|
+
}
|
|
4969
5020
|
|
|
4970
|
-
|
|
5021
|
+
get supportsSpaceInSearches() {
|
|
5022
|
+
return this.hasAttribute("supports-space-in-searches")
|
|
5023
|
+
}
|
|
4971
5024
|
|
|
4972
|
-
|
|
4973
|
-
|
|
5025
|
+
get open() {
|
|
5026
|
+
return this.popoverElement?.classList?.contains("lexxy-prompt-menu--visible")
|
|
4974
5027
|
}
|
|
4975
5028
|
|
|
4976
|
-
|
|
4977
|
-
|
|
5029
|
+
get closed() {
|
|
5030
|
+
return !this.open
|
|
5031
|
+
}
|
|
4978
5032
|
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
return `${action}Table${childTypeSuffix}${directionSuffix}`
|
|
5033
|
+
get #doesSpaceSelect() {
|
|
5034
|
+
return !this.supportsSpaceInSearches
|
|
4982
5035
|
}
|
|
4983
5036
|
|
|
4984
|
-
#
|
|
4985
|
-
const
|
|
4986
|
-
|
|
5037
|
+
#createSource() {
|
|
5038
|
+
const src = this.getAttribute("src");
|
|
5039
|
+
if (src) {
|
|
5040
|
+
if (this.hasAttribute("remote-filtering")) {
|
|
5041
|
+
return new RemoteFilterSource(src)
|
|
5042
|
+
} else {
|
|
5043
|
+
return new DeferredPromptSource(src)
|
|
5044
|
+
}
|
|
5045
|
+
} else {
|
|
5046
|
+
return new InlinePromptSource(this.querySelectorAll("lexxy-prompt-item"))
|
|
5047
|
+
}
|
|
4987
5048
|
}
|
|
4988
5049
|
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
5050
|
+
#addTriggerListener() {
|
|
5051
|
+
const unregister = this.#editor.registerUpdateListener(({ editorState }) => {
|
|
5052
|
+
editorState.read(() => {
|
|
5053
|
+
const { node, offset } = this.#selection.selectedNodeWithOffset();
|
|
5054
|
+
if (!node) return
|
|
4992
5055
|
|
|
4993
|
-
|
|
5056
|
+
if ($isTextNode(node)) {
|
|
5057
|
+
const fullText = node.getTextContent();
|
|
5058
|
+
const triggerLength = this.trigger.length;
|
|
4994
5059
|
|
|
4995
|
-
|
|
4996
|
-
|
|
5060
|
+
// Check if we have enough characters for the trigger
|
|
5061
|
+
if (offset >= triggerLength) {
|
|
5062
|
+
const textBeforeCursor = fullText.slice(offset - triggerLength, offset);
|
|
4997
5063
|
|
|
4998
|
-
|
|
4999
|
-
|
|
5064
|
+
// Check if trigger is at the start of the text node (new line case) or preceded by space or newline
|
|
5065
|
+
if (textBeforeCursor === this.trigger) {
|
|
5066
|
+
const isAtStart = offset === triggerLength;
|
|
5000
5067
|
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5068
|
+
const charBeforeTrigger = offset > triggerLength ? fullText[offset - triggerLength - 1] : null;
|
|
5069
|
+
const isPrecededBySpaceOrNewline = charBeforeTrigger === " " || charBeforeTrigger === "\n";
|
|
5070
|
+
|
|
5071
|
+
if (isAtStart || isPrecededBySpaceOrNewline) {
|
|
5072
|
+
unregister();
|
|
5073
|
+
this.#showPopover();
|
|
5074
|
+
}
|
|
5075
|
+
}
|
|
5076
|
+
}
|
|
5077
|
+
}
|
|
5078
|
+
});
|
|
5004
5079
|
});
|
|
5005
5080
|
}
|
|
5006
5081
|
|
|
5007
|
-
#
|
|
5008
|
-
|
|
5082
|
+
#addCursorPositionListener() {
|
|
5083
|
+
this.cursorPositionListener = this.#editor.registerUpdateListener(() => {
|
|
5084
|
+
if (this.closed) return
|
|
5009
5085
|
|
|
5010
|
-
|
|
5011
|
-
|
|
5086
|
+
this.#editor.read(() => {
|
|
5087
|
+
const { node, offset } = this.#selection.selectedNodeWithOffset();
|
|
5088
|
+
if (!node) return
|
|
5012
5089
|
|
|
5013
|
-
|
|
5014
|
-
|
|
5090
|
+
if ($isTextNode(node) && offset > 0) {
|
|
5091
|
+
const fullText = node.getTextContent();
|
|
5092
|
+
const textBeforeCursor = fullText.slice(0, offset);
|
|
5093
|
+
const lastTriggerIndex = textBeforeCursor.lastIndexOf(this.trigger);
|
|
5094
|
+
const triggerEndIndex = lastTriggerIndex + this.trigger.length - 1;
|
|
5015
5095
|
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5096
|
+
// If trigger is not found, or cursor is at or before the trigger end position, hide popover
|
|
5097
|
+
if (lastTriggerIndex === -1 || offset <= triggerEndIndex) {
|
|
5098
|
+
this.#hidePopover();
|
|
5099
|
+
}
|
|
5100
|
+
} else {
|
|
5101
|
+
// Cursor is not in a text node or at offset 0, hide popover
|
|
5102
|
+
this.#hidePopover();
|
|
5103
|
+
}
|
|
5104
|
+
});
|
|
5105
|
+
});
|
|
5106
|
+
}
|
|
5021
5107
|
|
|
5022
|
-
|
|
5108
|
+
#removeCursorPositionListener() {
|
|
5109
|
+
if (this.cursorPositionListener) {
|
|
5110
|
+
this.cursorPositionListener();
|
|
5111
|
+
this.cursorPositionListener = null;
|
|
5112
|
+
}
|
|
5023
5113
|
}
|
|
5024
5114
|
|
|
5025
|
-
#
|
|
5026
|
-
|
|
5027
|
-
|
|
5115
|
+
get #editor() {
|
|
5116
|
+
return this.#editorElement.editor
|
|
5117
|
+
}
|
|
5028
5118
|
|
|
5029
|
-
|
|
5030
|
-
|
|
5119
|
+
get #editorElement() {
|
|
5120
|
+
return this.closest("lexxy-editor")
|
|
5121
|
+
}
|
|
5031
5122
|
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
});
|
|
5123
|
+
get #selection() {
|
|
5124
|
+
return this.#editorElement.selection
|
|
5035
5125
|
}
|
|
5036
5126
|
|
|
5037
|
-
#
|
|
5038
|
-
|
|
5039
|
-
|
|
5127
|
+
async #showPopover() {
|
|
5128
|
+
this.popoverElement ??= await this.#buildPopover();
|
|
5129
|
+
this.#resetPopoverPosition();
|
|
5130
|
+
await this.#filterOptions();
|
|
5131
|
+
this.popoverElement.classList.toggle("lexxy-prompt-menu--visible", true);
|
|
5132
|
+
this.#selectFirstOption();
|
|
5040
5133
|
|
|
5041
|
-
this.
|
|
5042
|
-
|
|
5043
|
-
});
|
|
5044
|
-
}
|
|
5134
|
+
this.#editorElement.addEventListener("keydown", this.#handleKeydownOnPopover);
|
|
5135
|
+
this.#editorElement.addEventListener("lexxy:change", this.#filterOptions);
|
|
5045
5136
|
|
|
5046
|
-
|
|
5047
|
-
this
|
|
5137
|
+
this.#registerKeyListeners();
|
|
5138
|
+
this.#addCursorPositionListener();
|
|
5048
5139
|
}
|
|
5049
5140
|
|
|
5050
|
-
#
|
|
5051
|
-
|
|
5052
|
-
|
|
5141
|
+
#registerKeyListeners() {
|
|
5142
|
+
// We can't use a regular keydown for Enter as Lexical handles it first
|
|
5143
|
+
this.keyListeners.push(this.#editor.registerCommand(KEY_ENTER_COMMAND, this.#handleSelectedOption.bind(this), COMMAND_PRIORITY_HIGH));
|
|
5144
|
+
this.keyListeners.push(this.#editor.registerCommand(KEY_TAB_COMMAND, this.#handleSelectedOption.bind(this), COMMAND_PRIORITY_HIGH));
|
|
5053
5145
|
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5146
|
+
if (this.#doesSpaceSelect) {
|
|
5147
|
+
this.keyListeners.push(this.#editor.registerCommand(KEY_SPACE_COMMAND, this.#handleSelectedOption.bind(this), COMMAND_PRIORITY_HIGH));
|
|
5148
|
+
}
|
|
5057
5149
|
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
next.selectStart();
|
|
5062
|
-
} else {
|
|
5063
|
-
const newParagraph = $createParagraphNode();
|
|
5064
|
-
this.currentTableNode.insertAfter(newParagraph);
|
|
5065
|
-
newParagraph.selectStart();
|
|
5066
|
-
}
|
|
5067
|
-
});
|
|
5150
|
+
// Register arrow keys with HIGH priority to prevent Lexical's selection handlers from running
|
|
5151
|
+
this.keyListeners.push(this.#editor.registerCommand(KEY_ARROW_UP_COMMAND, this.#handleArrowUp.bind(this), COMMAND_PRIORITY_HIGH));
|
|
5152
|
+
this.keyListeners.push(this.#editor.registerCommand(KEY_ARROW_DOWN_COMMAND, this.#handleArrowDown.bind(this), COMMAND_PRIORITY_HIGH));
|
|
5068
5153
|
}
|
|
5069
5154
|
|
|
5070
|
-
#
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
if (!cell) return false
|
|
5075
|
-
|
|
5076
|
-
return cell.getTextContent().trim() === ""
|
|
5155
|
+
#handleArrowUp(event) {
|
|
5156
|
+
this.#moveSelectionUp();
|
|
5157
|
+
event.preventDefault();
|
|
5158
|
+
return true
|
|
5077
5159
|
}
|
|
5078
5160
|
|
|
5079
|
-
#
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
if (!rows) return false
|
|
5084
|
-
|
|
5085
|
-
return rows.length === this.currentRowIndex + 1
|
|
5161
|
+
#handleArrowDown(event) {
|
|
5162
|
+
this.#moveSelectionDown();
|
|
5163
|
+
event.preventDefault();
|
|
5164
|
+
return true
|
|
5086
5165
|
}
|
|
5087
5166
|
|
|
5088
|
-
#
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
const cells = this.currentRowCells;
|
|
5092
|
-
if (!cells) return false
|
|
5167
|
+
#selectFirstOption() {
|
|
5168
|
+
const firstOption = this.#listItemElements[0];
|
|
5093
5169
|
|
|
5094
|
-
|
|
5170
|
+
if (firstOption) {
|
|
5171
|
+
this.#selectOption(firstOption);
|
|
5172
|
+
}
|
|
5095
5173
|
}
|
|
5096
5174
|
|
|
5097
|
-
#
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
const cells = this.currentRowCells;
|
|
5101
|
-
if (!cells) return false
|
|
5102
|
-
|
|
5103
|
-
return cells.indexOf(this.currentCell) === 0
|
|
5175
|
+
get #listItemElements() {
|
|
5176
|
+
return Array.from(this.popoverElement.querySelectorAll(".lexxy-prompt-menu__item"))
|
|
5104
5177
|
}
|
|
5105
5178
|
|
|
5106
|
-
#
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5179
|
+
#selectOption(listItem) {
|
|
5180
|
+
this.#clearSelection();
|
|
5181
|
+
listItem.toggleAttribute("aria-selected", true);
|
|
5182
|
+
listItem.scrollIntoView({ block: "nearest", behavior: "smooth" });
|
|
5183
|
+
listItem.focus();
|
|
5111
5184
|
|
|
5112
|
-
|
|
5113
|
-
this.
|
|
5114
|
-
|
|
5185
|
+
// Preserve selection to prevent cursor jump
|
|
5186
|
+
this.#selection.preservingSelection(() => {
|
|
5187
|
+
this.#editorElement.focus();
|
|
5188
|
+
});
|
|
5115
5189
|
|
|
5116
|
-
this.
|
|
5117
|
-
this.
|
|
5190
|
+
this.#editorContentElement.setAttribute("aria-controls", this.popoverElement.id);
|
|
5191
|
+
this.#editorContentElement.setAttribute("aria-activedescendant", listItem.id);
|
|
5192
|
+
this.#editorContentElement.setAttribute("aria-haspopup", "listbox");
|
|
5118
5193
|
}
|
|
5119
5194
|
|
|
5120
|
-
#
|
|
5121
|
-
|
|
5195
|
+
#clearSelection() {
|
|
5196
|
+
this.#listItemElements.forEach((item) => { item.toggleAttribute("aria-selected", false); });
|
|
5197
|
+
this.#editorContentElement.removeAttribute("aria-controls");
|
|
5198
|
+
this.#editorContentElement.removeAttribute("aria-activedescendant");
|
|
5199
|
+
this.#editorContentElement.removeAttribute("aria-haspopup");
|
|
5200
|
+
}
|
|
5122
5201
|
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5202
|
+
#positionPopover() {
|
|
5203
|
+
const { x, y, fontSize } = this.#selection.cursorPosition;
|
|
5204
|
+
const editorRect = this.#editorElement.getBoundingClientRect();
|
|
5205
|
+
const contentRect = this.#editorContentElement.getBoundingClientRect();
|
|
5206
|
+
const verticalOffset = contentRect.top - editorRect.top;
|
|
5128
5207
|
|
|
5129
|
-
if (
|
|
5130
|
-
|
|
5131
|
-
this
|
|
5132
|
-
return true
|
|
5208
|
+
if (!this.popoverElement.hasAttribute("data-anchored")) {
|
|
5209
|
+
this.popoverElement.style.left = `${x}px`;
|
|
5210
|
+
this.popoverElement.toggleAttribute("data-anchored", true);
|
|
5133
5211
|
}
|
|
5134
5212
|
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
#handleEnterKey(event) {
|
|
5139
|
-
if ((event.ctrlKey || event.metaKey) || event.shiftKey || !this.currentTableNode) return false
|
|
5140
|
-
|
|
5141
|
-
if (this.selection.isInsideList || this.selection.isInsideCodeBlock) return false
|
|
5213
|
+
this.popoverElement.style.top = `${y + verticalOffset}px`;
|
|
5214
|
+
this.popoverElement.style.bottom = "auto";
|
|
5142
5215
|
|
|
5143
|
-
|
|
5216
|
+
const popoverRect = this.popoverElement.getBoundingClientRect();
|
|
5217
|
+
const isClippedAtBottom = popoverRect.bottom > window.innerHeight;
|
|
5144
5218
|
|
|
5145
|
-
if (
|
|
5146
|
-
this
|
|
5147
|
-
|
|
5148
|
-
this
|
|
5149
|
-
} else {
|
|
5150
|
-
this.#selectNextRow();
|
|
5219
|
+
if (isClippedAtBottom || this.popoverElement.hasAttribute("data-clipped-at-bottom")) {
|
|
5220
|
+
this.popoverElement.style.top = `${y + verticalOffset - popoverRect.height - fontSize}px`;
|
|
5221
|
+
this.popoverElement.style.bottom = "auto";
|
|
5222
|
+
this.popoverElement.toggleAttribute("data-clipped-at-bottom", true);
|
|
5151
5223
|
}
|
|
5152
|
-
|
|
5153
|
-
return true
|
|
5154
5224
|
}
|
|
5155
|
-
}
|
|
5156
|
-
|
|
5157
|
-
var TableIcons = {
|
|
5158
|
-
"insert-row-before":
|
|
5159
|
-
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5160
|
-
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.86804e-07 15C8.29055e-07 15.8284 0.671574 16.5 1.5 16.5H15L15.1533 16.4922C15.8593 16.4205 16.4205 15.8593 16.4922 15.1533L16.5 15V4.5L16.4922 4.34668C16.4154 3.59028 15.7767 3 15 3H13.5L13.5 4.5H15V9H1.5L1.5 4.5L3 4.5V3H1.5C0.671574 3 1.20956e-06 3.67157 1.24577e-06 4.5L7.86804e-07 15ZM15 10.5V15H1.5L1.5 10.5H15Z"/>
|
|
5161
|
-
<path d="M4.5 4.5H7.5V7.5H9V4.5H12L12 3L9 3V6.55671e-08L7.5 0V3L4.5 3V4.5Z"/>
|
|
5162
|
-
</svg>`,
|
|
5163
|
-
|
|
5164
|
-
"insert-row-after":
|
|
5165
|
-
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5166
|
-
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.86804e-07 13.5C7.50592e-07 14.3284 0.671574 15 1.5 15H3V13.5H1.5L1.5 9L15 9V13.5H13.5V15H15C15.7767 15 16.4154 14.4097 16.4922 13.6533L16.5 13.5V3L16.4922 2.84668C16.4205 2.14069 15.8593 1.57949 15.1533 1.50781L15 1.5L1.5 1.5C0.671574 1.5 1.28803e-06 2.17157 1.24577e-06 3L7.86804e-07 13.5ZM15 3V7.5L1.5 7.5L1.5 3L15 3Z"/>
|
|
5167
|
-
<path d="M7.5 15V18H9V15H12V13.5H9V10.5H7.5V13.5H4.5V15H7.5Z"/>
|
|
5168
|
-
</svg>`,
|
|
5169
|
-
|
|
5170
|
-
"delete-row":
|
|
5171
|
-
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5172
|
-
<path d="M16.4922 12.1533C16.4154 12.9097 15.7767 13.5 15 13.5L12 13.5V12H15V6L1.5 6L1.5 12H4.5V13.5H1.5C0.723337 13.5 0.0846104 12.9097 0.00781328 12.1533L7.86804e-07 12L1.04907e-06 6C1.17362e-06 5.22334 0.590278 4.58461 1.34668 4.50781L1.5 4.5L15 4.5C15.8284 4.5 16.5 5.17157 16.5 6V12L16.4922 12.1533Z"/>
|
|
5173
|
-
<path d="M10.3711 15.9316L8.25 13.8096L6.12793 15.9316L5.06738 14.8711L7.18945 12.75L5.06738 10.6289L6.12793 9.56836L8.25 11.6895L10.3711 9.56836L11.4316 10.6289L9.31055 12.75L11.4316 14.8711L10.3711 15.9316Z"/>
|
|
5174
|
-
</svg>`,
|
|
5175
|
-
|
|
5176
|
-
"toggle-row":
|
|
5177
|
-
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5178
|
-
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.00781328 13.6533C0.0846108 14.4097 0.723337 15 1.5 15L15 15L15.1533 14.9922C15.8593 14.9205 16.4205 14.3593 16.4922 13.6533L16.5 13.5V4.5L16.4922 4.34668C16.4205 3.64069 15.8593 3.07949 15.1533 3.00781L15 3L1.5 3C0.671574 3 1.24863e-06 3.67157 1.18021e-06 4.5L7.86804e-07 13.5L0.00781328 13.6533ZM15 9V13.5L1.5 13.5L1.5 9L15 9Z"/>
|
|
5179
|
-
</svg>`,
|
|
5180
|
-
|
|
5181
|
-
"insert-column-before":
|
|
5182
|
-
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5183
|
-
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.5 0C3.67157 0 3 0.671573 3 1.5V3H4.5V1.5H9V15H4.5V13.5H3V15C3 15.7767 3.59028 16.4154 4.34668 16.4922L4.5 16.5H15L15.1533 16.4922C15.8593 16.4205 16.4205 15.8593 16.4922 15.1533L16.5 15V1.5C16.5 0.671573 15.8284 6.03989e-09 15 0H4.5ZM15 15H10.5V1.5H15V15Z"/>
|
|
5184
|
-
<path d="M3 7.5H0V9H3V12H4.5V9H7.5V7.5H4.5V4.5H3V7.5Z"/>
|
|
5185
|
-
</svg>`,
|
|
5186
|
-
|
|
5187
|
-
"insert-column-after":
|
|
5188
|
-
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5189
|
-
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.5 0C14.3284 0 15 0.671573 15 1.5V3H13.5V1.5H9V15H13.5V13.5H15V15C15 15.7767 14.4097 16.4154 13.6533 16.4922L13.5 16.5H3L2.84668 16.4922C2.14069 16.4205 1.57949 15.8593 1.50781 15.1533L1.5 15V1.5C1.5 0.671573 2.17157 6.03989e-09 3 0H13.5ZM3 15H7.5V1.5H3V15Z"/>
|
|
5190
|
-
<path d="M15 7.5H18V9H15V12H13.5V9H10.5V7.5H13.5V4.5H15V7.5Z"/>
|
|
5191
|
-
</svg>`,
|
|
5192
5225
|
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
</svg>`,
|
|
5226
|
+
#resetPopoverPosition() {
|
|
5227
|
+
this.popoverElement.removeAttribute("data-clipped-at-bottom");
|
|
5228
|
+
this.popoverElement.removeAttribute("data-anchored");
|
|
5229
|
+
}
|
|
5198
5230
|
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5231
|
+
async #hidePopover() {
|
|
5232
|
+
this.#clearSelection();
|
|
5233
|
+
this.popoverElement.classList.toggle("lexxy-prompt-menu--visible", false);
|
|
5234
|
+
this.#editorElement.removeEventListener("lexxy:change", this.#filterOptions);
|
|
5235
|
+
this.#editorElement.removeEventListener("keydown", this.#handleKeydownOnPopover);
|
|
5203
5236
|
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
<path d="M18.2129 19.2305C18.0925 20.7933 16.7892 22 15.2217 22H7.77832C6.21084 22 4.90753 20.7933 4.78711 19.2305L4 9H19L18.2129 19.2305Z"/><path d="M13 2C14.1046 2 15 2.89543 15 4H19C19.5523 4 20 4.44772 20 5V6C20 6.55228 19.5523 7 19 7H4C3.44772 7 3 6.55228 3 6V5C3 4.44772 3.44772 4 4 4H8C8 2.89543 8.89543 2 10 2H13Z"/>
|
|
5207
|
-
</svg>`
|
|
5208
|
-
};
|
|
5237
|
+
this.#unregisterKeyListeners();
|
|
5238
|
+
this.#removeCursorPositionListener();
|
|
5209
5239
|
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5240
|
+
await nextFrame();
|
|
5241
|
+
this.#addTriggerListener();
|
|
5242
|
+
}
|
|
5213
5243
|
|
|
5214
|
-
|
|
5215
|
-
this
|
|
5216
|
-
this
|
|
5244
|
+
#unregisterKeyListeners() {
|
|
5245
|
+
this.keyListeners.forEach((unregister) => unregister());
|
|
5246
|
+
this.keyListeners = [];
|
|
5217
5247
|
}
|
|
5218
5248
|
|
|
5219
|
-
|
|
5220
|
-
this
|
|
5249
|
+
#filterOptions = async () => {
|
|
5250
|
+
if (this.initialPrompt) {
|
|
5251
|
+
this.initialPrompt = false;
|
|
5252
|
+
return
|
|
5253
|
+
}
|
|
5221
5254
|
|
|
5222
|
-
this.
|
|
5223
|
-
|
|
5255
|
+
if (this.#editorContents.containsTextBackUntil(this.trigger)) {
|
|
5256
|
+
await this.#showFilteredOptions();
|
|
5257
|
+
await nextFrame();
|
|
5258
|
+
this.#positionPopover();
|
|
5259
|
+
} else {
|
|
5260
|
+
this.#hidePopover();
|
|
5261
|
+
}
|
|
5262
|
+
}
|
|
5224
5263
|
|
|
5225
|
-
|
|
5264
|
+
async #showFilteredOptions() {
|
|
5265
|
+
const filter = this.#editorContents.textBackUntil(this.trigger);
|
|
5266
|
+
const filteredListItems = await this.source.buildListItems(filter);
|
|
5267
|
+
this.popoverElement.innerHTML = "";
|
|
5226
5268
|
|
|
5227
|
-
|
|
5228
|
-
|
|
5269
|
+
if (filteredListItems.length > 0) {
|
|
5270
|
+
this.#showResults(filteredListItems);
|
|
5271
|
+
} else {
|
|
5272
|
+
this.#showEmptyResults();
|
|
5273
|
+
}
|
|
5274
|
+
this.#selectFirstOption();
|
|
5229
5275
|
}
|
|
5230
5276
|
|
|
5231
|
-
|
|
5232
|
-
|
|
5277
|
+
#showResults(filteredListItems) {
|
|
5278
|
+
this.popoverElement.classList.remove("lexxy-prompt-menu--empty");
|
|
5279
|
+
this.popoverElement.append(...filteredListItems);
|
|
5233
5280
|
}
|
|
5234
5281
|
|
|
5235
|
-
|
|
5236
|
-
|
|
5282
|
+
#showEmptyResults() {
|
|
5283
|
+
this.popoverElement.classList.add("lexxy-prompt-menu--empty");
|
|
5284
|
+
const el = createElement("li", { innerHTML: this.#emptyResultsMessage });
|
|
5285
|
+
el.classList.add("lexxy-prompt-menu__item--empty");
|
|
5286
|
+
this.popoverElement.append(el);
|
|
5237
5287
|
}
|
|
5238
5288
|
|
|
5239
|
-
get #
|
|
5240
|
-
return
|
|
5289
|
+
get #emptyResultsMessage() {
|
|
5290
|
+
return this.getAttribute("empty-results") || NOTHING_FOUND_DEFAULT_MESSAGE
|
|
5241
5291
|
}
|
|
5242
5292
|
|
|
5243
|
-
#
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5293
|
+
#handleKeydownOnPopover = (event) => {
|
|
5294
|
+
if (event.key === "Escape") {
|
|
5295
|
+
this.#hidePopover();
|
|
5296
|
+
this.#editorElement.focus();
|
|
5297
|
+
event.stopPropagation();
|
|
5298
|
+
}
|
|
5299
|
+
// Arrow keys are now handled via Lexical commands with HIGH priority
|
|
5249
5300
|
}
|
|
5250
5301
|
|
|
5251
|
-
#
|
|
5252
|
-
const
|
|
5253
|
-
|
|
5254
|
-
const plusButton = this.#createButton(`Add ${childType}`, { action: "insert", childType, direction: "after" }, "+");
|
|
5255
|
-
const minusButton = this.#createButton(`Remove ${childType}`, { action: "delete", childType }, "−");
|
|
5256
|
-
|
|
5257
|
-
const dropdown = createElement("details", { className: "lexxy-table-control__more-menu" });
|
|
5258
|
-
dropdown.setAttribute("name", "lexxy-dropdown");
|
|
5259
|
-
dropdown.tabIndex = -1;
|
|
5260
|
-
|
|
5261
|
-
const count = createElement("summary", {}, `_ ${childType}s`);
|
|
5262
|
-
setCountProperty(count);
|
|
5263
|
-
dropdown.appendChild(count);
|
|
5264
|
-
|
|
5265
|
-
dropdown.appendChild(moreMenu);
|
|
5266
|
-
|
|
5267
|
-
container.appendChild(minusButton);
|
|
5268
|
-
container.appendChild(dropdown);
|
|
5269
|
-
container.appendChild(plusButton);
|
|
5270
|
-
|
|
5271
|
-
return container
|
|
5302
|
+
#moveSelectionDown() {
|
|
5303
|
+
const nextIndex = this.#selectedIndex + 1;
|
|
5304
|
+
if (nextIndex < this.#listItemElements.length) this.#selectOption(this.#listItemElements[nextIndex]);
|
|
5272
5305
|
}
|
|
5273
5306
|
|
|
5274
|
-
#
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
(count) => { this.rowCount = count; },
|
|
5278
|
-
this.#createMoreMenuSection("row")
|
|
5279
|
-
)
|
|
5307
|
+
#moveSelectionUp() {
|
|
5308
|
+
const previousIndex = this.#selectedIndex - 1;
|
|
5309
|
+
if (previousIndex >= 0) this.#selectOption(this.#listItemElements[previousIndex]);
|
|
5280
5310
|
}
|
|
5281
5311
|
|
|
5282
|
-
#
|
|
5283
|
-
return this.#
|
|
5284
|
-
"column",
|
|
5285
|
-
(count) => { this.columnCount = count; },
|
|
5286
|
-
this.#createMoreMenuSection("column")
|
|
5287
|
-
)
|
|
5312
|
+
get #selectedIndex() {
|
|
5313
|
+
return this.#listItemElements.findIndex((item) => item.hasAttribute("aria-selected"))
|
|
5288
5314
|
}
|
|
5289
5315
|
|
|
5290
|
-
#
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
const addAfterButton = this.#createButton(`Add ${childType} after`, { action: "insert", childType, direction: "after" });
|
|
5294
|
-
const toggleStyleButton = this.#createButton(`Toggle ${childType} style`, { action: "toggle", childType });
|
|
5295
|
-
const deleteButton = this.#createButton(`Remove ${childType}`, { action: "delete", childType });
|
|
5316
|
+
get #selectedListItem() {
|
|
5317
|
+
return this.#listItemElements[this.#selectedIndex]
|
|
5318
|
+
}
|
|
5296
5319
|
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5320
|
+
#handleSelectedOption(event) {
|
|
5321
|
+
event.preventDefault();
|
|
5322
|
+
event.stopPropagation();
|
|
5323
|
+
this.#optionWasSelected();
|
|
5324
|
+
return true
|
|
5325
|
+
}
|
|
5301
5326
|
|
|
5302
|
-
|
|
5327
|
+
#optionWasSelected() {
|
|
5328
|
+
this.#replaceTriggerWithSelectedItem();
|
|
5329
|
+
this.#hidePopover();
|
|
5330
|
+
this.#editorElement.focus();
|
|
5303
5331
|
}
|
|
5304
5332
|
|
|
5305
|
-
#
|
|
5306
|
-
const
|
|
5333
|
+
#replaceTriggerWithSelectedItem() {
|
|
5334
|
+
const promptItem = this.source.promptItemFor(this.#selectedListItem);
|
|
5307
5335
|
|
|
5308
|
-
|
|
5309
|
-
deleteTableButton.classList.add("lexxy-table-control__button--delete-table");
|
|
5336
|
+
if (!promptItem) { return }
|
|
5310
5337
|
|
|
5311
|
-
|
|
5338
|
+
const templates = Array.from(promptItem.querySelectorAll("template[type='editor']"));
|
|
5339
|
+
const stringToReplace = `${this.trigger}${this.#editorContents.textBackUntil(this.trigger)}`;
|
|
5312
5340
|
|
|
5313
|
-
this.
|
|
5341
|
+
if (this.hasAttribute("insert-editable-text")) {
|
|
5342
|
+
this.#insertTemplatesAsEditableText(templates, stringToReplace);
|
|
5343
|
+
} else {
|
|
5344
|
+
this.#insertTemplatesAsAttachments(templates, stringToReplace, promptItem.getAttribute("sgid"));
|
|
5345
|
+
}
|
|
5346
|
+
}
|
|
5314
5347
|
|
|
5315
|
-
|
|
5348
|
+
#insertTemplatesAsEditableText(templates, stringToReplace) {
|
|
5349
|
+
this.#editor.update(() => {
|
|
5350
|
+
const nodes = templates.flatMap(template => this.#buildEditableTextNodes(template));
|
|
5351
|
+
this.#editorContents.replaceTextBackUntil(stringToReplace, nodes);
|
|
5352
|
+
});
|
|
5316
5353
|
}
|
|
5317
5354
|
|
|
5318
|
-
#
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5355
|
+
#buildEditableTextNodes(template) {
|
|
5356
|
+
return $generateNodesFromDOM(this.#editor, parseHtml(`${template.innerHTML}`))
|
|
5357
|
+
}
|
|
5358
|
+
|
|
5359
|
+
#insertTemplatesAsAttachments(templates, stringToReplace, fallbackSgid = null) {
|
|
5360
|
+
this.#editor.update(() => {
|
|
5361
|
+
const attachmentNodes = this.#buildAttachmentNodes(templates, fallbackSgid);
|
|
5362
|
+
const spacedAttachmentNodes = attachmentNodes.flatMap(node => [ node, this.#getSpacerTextNode() ]).slice(0, -1);
|
|
5363
|
+
this.#editorContents.replaceTextBackUntil(stringToReplace, spacedAttachmentNodes);
|
|
5323
5364
|
});
|
|
5324
|
-
|
|
5325
|
-
|
|
5365
|
+
}
|
|
5366
|
+
|
|
5367
|
+
#buildAttachmentNodes(templates, fallbackSgid = null) {
|
|
5368
|
+
return templates.map(
|
|
5369
|
+
template => this.#buildAttachmentNode(
|
|
5370
|
+
template.innerHTML,
|
|
5371
|
+
template.getAttribute("content-type") || this.#defaultPromptContentType,
|
|
5372
|
+
template.getAttribute("sgid") || fallbackSgid
|
|
5373
|
+
))
|
|
5374
|
+
}
|
|
5326
5375
|
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5376
|
+
#getSpacerTextNode() {
|
|
5377
|
+
return $createTextNode(" ")
|
|
5378
|
+
}
|
|
5330
5379
|
|
|
5331
|
-
|
|
5380
|
+
get #defaultPromptContentType() {
|
|
5381
|
+
const attachmentContentTypeNamespace = Lexxy.global.get("attachmentContentTypeNamespace");
|
|
5382
|
+
return `application/vnd.${attachmentContentTypeNamespace}.${this.name}`
|
|
5383
|
+
}
|
|
5332
5384
|
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5385
|
+
#buildAttachmentNode(innerHtml, contentType, sgid) {
|
|
5386
|
+
return new CustomActionTextAttachmentNode({ sgid, contentType, innerHtml })
|
|
5387
|
+
}
|
|
5336
5388
|
|
|
5337
|
-
|
|
5389
|
+
get #editorContents() {
|
|
5390
|
+
return this.#editorElement.contents
|
|
5338
5391
|
}
|
|
5339
5392
|
|
|
5340
|
-
#
|
|
5341
|
-
|
|
5393
|
+
get #editorContentElement() {
|
|
5394
|
+
return this.#editorElement.editorContentElement
|
|
5342
5395
|
}
|
|
5343
5396
|
|
|
5344
|
-
#
|
|
5345
|
-
|
|
5346
|
-
|
|
5397
|
+
async #buildPopover() {
|
|
5398
|
+
const popoverContainer = createElement("ul", { role: "listbox", id: generateDomId("prompt-popover") }); // Avoiding [popover] due to not being able to position at an arbitrary X, Y position.
|
|
5399
|
+
popoverContainer.classList.add("lexxy-prompt-menu");
|
|
5400
|
+
popoverContainer.style.position = "absolute";
|
|
5401
|
+
popoverContainer.setAttribute("nonce", getNonce());
|
|
5402
|
+
popoverContainer.append(...await this.source.buildListItems());
|
|
5403
|
+
popoverContainer.addEventListener("click", this.#handlePopoverClick);
|
|
5404
|
+
this.#editorElement.appendChild(popoverContainer);
|
|
5405
|
+
return popoverContainer
|
|
5347
5406
|
}
|
|
5348
5407
|
|
|
5349
|
-
#
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5408
|
+
#handlePopoverClick = (event) => {
|
|
5409
|
+
const listItem = event.target.closest(".lexxy-prompt-menu__item");
|
|
5410
|
+
if (listItem) {
|
|
5411
|
+
this.#selectOption(listItem);
|
|
5412
|
+
this.#optionWasSelected();
|
|
5353
5413
|
}
|
|
5354
5414
|
}
|
|
5355
5415
|
|
|
5356
|
-
#
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
} else {
|
|
5360
|
-
handleRollingTabIndex(this.#tableToolsButtons, event);
|
|
5361
|
-
}
|
|
5416
|
+
#reconnect() {
|
|
5417
|
+
this.disconnectedCallback();
|
|
5418
|
+
this.connectedCallback();
|
|
5362
5419
|
}
|
|
5420
|
+
}
|
|
5363
5421
|
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5422
|
+
class CodeLanguagePicker extends HTMLElement {
|
|
5423
|
+
connectedCallback() {
|
|
5424
|
+
this.editorElement = this.closest("lexxy-editor");
|
|
5425
|
+
this.editor = this.editorElement.editor;
|
|
5367
5426
|
|
|
5368
|
-
this.#
|
|
5369
|
-
|
|
5370
|
-
|
|
5427
|
+
this.#attachLanguagePicker();
|
|
5428
|
+
this.#monitorForCodeBlockSelection();
|
|
5429
|
+
}
|
|
5430
|
+
|
|
5431
|
+
#attachLanguagePicker() {
|
|
5432
|
+
this.languagePickerElement = this.#createLanguagePicker();
|
|
5433
|
+
|
|
5434
|
+
this.languagePickerElement.addEventListener("change", () => {
|
|
5435
|
+
this.#updateCodeBlockLanguage(this.languagePickerElement.value);
|
|
5371
5436
|
});
|
|
5372
5437
|
|
|
5373
|
-
this
|
|
5438
|
+
this.languagePickerElement.setAttribute("nonce", getNonce());
|
|
5439
|
+
this.appendChild(this.languagePickerElement);
|
|
5374
5440
|
}
|
|
5375
5441
|
|
|
5376
|
-
|
|
5377
|
-
|
|
5442
|
+
#createLanguagePicker() {
|
|
5443
|
+
const selectElement = createElement("select", { className: "lexxy-code-language-picker", "aria-label": "Pick a language…", name: "lexxy-code-language" });
|
|
5378
5444
|
|
|
5379
|
-
this.#
|
|
5445
|
+
for (const [ value, label ] of Object.entries(this.#languages)) {
|
|
5446
|
+
const option = document.createElement("option");
|
|
5447
|
+
option.value = value;
|
|
5448
|
+
option.textContent = label;
|
|
5449
|
+
selectElement.appendChild(option);
|
|
5450
|
+
}
|
|
5380
5451
|
|
|
5381
|
-
|
|
5382
|
-
|
|
5452
|
+
return selectElement
|
|
5453
|
+
}
|
|
5383
5454
|
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
childType: activeElement.dataset.childType,
|
|
5387
|
-
direction: activeElement.dataset.direction
|
|
5388
|
-
};
|
|
5455
|
+
get #languages() {
|
|
5456
|
+
const languages = { ...CODE_LANGUAGE_FRIENDLY_NAME_MAP };
|
|
5389
5457
|
|
|
5390
|
-
|
|
5458
|
+
if (!languages.ruby) languages.ruby = "Ruby";
|
|
5459
|
+
if (!languages.php) languages.php = "PHP";
|
|
5460
|
+
if (!languages.go) languages.go = "Go";
|
|
5461
|
+
if (!languages.bash) languages.bash = "Bash";
|
|
5462
|
+
if (!languages.json) languages.json = "JSON";
|
|
5463
|
+
if (!languages.diff) languages.diff = "Diff";
|
|
5391
5464
|
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
cellsToHighlight = this.tableController.currentRowCells;
|
|
5395
|
-
break
|
|
5396
|
-
case "column":
|
|
5397
|
-
cellsToHighlight = this.tableController.currentColumnCells;
|
|
5398
|
-
break
|
|
5399
|
-
case "table":
|
|
5400
|
-
cellsToHighlight = this.tableController.tableRows;
|
|
5401
|
-
break
|
|
5402
|
-
}
|
|
5465
|
+
const sortedEntries = Object.entries(languages)
|
|
5466
|
+
.sort(([ , a ], [ , b ]) => a.localeCompare(b));
|
|
5403
5467
|
|
|
5404
|
-
|
|
5468
|
+
// Place the "plain" entry first, then the rest of language sorted alphabetically
|
|
5469
|
+
const plainIndex = sortedEntries.findIndex(([ key ]) => key === "plain");
|
|
5470
|
+
const plainEntry = sortedEntries.splice(plainIndex, 1)[0];
|
|
5471
|
+
return Object.fromEntries([ plainEntry, ...sortedEntries ])
|
|
5472
|
+
}
|
|
5405
5473
|
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5474
|
+
#updateCodeBlockLanguage(language) {
|
|
5475
|
+
this.editor.update(() => {
|
|
5476
|
+
const codeNode = this.#getCurrentCodeNode();
|
|
5409
5477
|
|
|
5410
|
-
|
|
5411
|
-
|
|
5478
|
+
if (codeNode) {
|
|
5479
|
+
codeNode.setLanguage(language);
|
|
5480
|
+
}
|
|
5412
5481
|
});
|
|
5413
5482
|
}
|
|
5414
5483
|
|
|
5415
|
-
#
|
|
5416
|
-
this.
|
|
5417
|
-
this.
|
|
5484
|
+
#monitorForCodeBlockSelection() {
|
|
5485
|
+
this.editor.registerUpdateListener(() => {
|
|
5486
|
+
this.editor.getEditorState().read(() => {
|
|
5487
|
+
const codeNode = this.#getCurrentCodeNode();
|
|
5418
5488
|
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
}
|
|
5489
|
+
if (codeNode) {
|
|
5490
|
+
this.#codeNodeWasSelected(codeNode);
|
|
5491
|
+
} else {
|
|
5492
|
+
this.#hideLanguagePicker();
|
|
5493
|
+
}
|
|
5494
|
+
});
|
|
5425
5495
|
});
|
|
5426
5496
|
}
|
|
5427
5497
|
|
|
5428
|
-
#
|
|
5429
|
-
|
|
5430
|
-
this.#update();
|
|
5431
|
-
}
|
|
5498
|
+
#getCurrentCodeNode() {
|
|
5499
|
+
const selection = $getSelection();
|
|
5432
5500
|
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
}
|
|
5501
|
+
if (!$isRangeSelection(selection)) {
|
|
5502
|
+
return null
|
|
5503
|
+
}
|
|
5437
5504
|
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
this.#clearCellStyles();
|
|
5441
|
-
}
|
|
5505
|
+
const anchorNode = selection.anchor.getNode();
|
|
5506
|
+
const parentNode = anchorNode.getParent();
|
|
5442
5507
|
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5508
|
+
if ($isCodeNode(anchorNode)) {
|
|
5509
|
+
return anchorNode
|
|
5510
|
+
} else if ($isCodeNode(parentNode)) {
|
|
5511
|
+
return parentNode
|
|
5512
|
+
}
|
|
5513
|
+
|
|
5514
|
+
return null
|
|
5448
5515
|
}
|
|
5449
5516
|
|
|
5450
|
-
#
|
|
5451
|
-
|
|
5517
|
+
#codeNodeWasSelected(codeNode) {
|
|
5518
|
+
const language = codeNode.getLanguage();
|
|
5519
|
+
|
|
5520
|
+
this.#updateLanguagePickerWith(language);
|
|
5521
|
+
this.#showLanguagePicker();
|
|
5522
|
+
this.#positionLanguagePicker(codeNode);
|
|
5452
5523
|
}
|
|
5453
5524
|
|
|
5454
|
-
#
|
|
5455
|
-
|
|
5456
|
-
|
|
5525
|
+
#updateLanguagePickerWith(language) {
|
|
5526
|
+
if (this.languagePickerElement && language) {
|
|
5527
|
+
const normalizedLanguage = normalizeCodeLang(language);
|
|
5528
|
+
this.languagePickerElement.value = normalizedLanguage;
|
|
5529
|
+
}
|
|
5530
|
+
}
|
|
5457
5531
|
|
|
5458
|
-
|
|
5459
|
-
|
|
5532
|
+
#positionLanguagePicker(codeNode) {
|
|
5533
|
+
const codeElement = this.editor.getElementByKey(codeNode.getKey());
|
|
5534
|
+
if (!codeElement) return
|
|
5460
5535
|
|
|
5461
|
-
const
|
|
5462
|
-
const editorRect = this
|
|
5536
|
+
const codeRect = codeElement.getBoundingClientRect();
|
|
5537
|
+
const editorRect = this.editorElement.getBoundingClientRect();
|
|
5538
|
+
const relativeTop = codeRect.top - editorRect.top;
|
|
5539
|
+
const relativeRight = editorRect.right - codeRect.right;
|
|
5463
5540
|
|
|
5464
|
-
const relativeTop = tableRect.top - editorRect.top;
|
|
5465
|
-
const relativeCenter = (tableRect.left + tableRect.right) / 2 - editorRect.left;
|
|
5466
5541
|
this.style.top = `${relativeTop}px`;
|
|
5467
|
-
this.style.
|
|
5542
|
+
this.style.right = `${relativeRight}px`;
|
|
5468
5543
|
}
|
|
5469
5544
|
|
|
5470
|
-
#
|
|
5471
|
-
|
|
5472
|
-
|
|
5545
|
+
#showLanguagePicker() {
|
|
5546
|
+
this.hidden = false;
|
|
5547
|
+
}
|
|
5473
5548
|
|
|
5474
|
-
|
|
5475
|
-
|
|
5549
|
+
#hideLanguagePicker() {
|
|
5550
|
+
this.hidden = true;
|
|
5551
|
+
}
|
|
5552
|
+
}
|
|
5476
5553
|
|
|
5477
|
-
|
|
5478
|
-
|
|
5554
|
+
class TableController {
|
|
5555
|
+
constructor(editorElement) {
|
|
5556
|
+
this.editor = editorElement.editor;
|
|
5557
|
+
this.contents = editorElement.contents;
|
|
5558
|
+
this.selection = editorElement.selection;
|
|
5479
5559
|
|
|
5480
|
-
this.
|
|
5481
|
-
this.
|
|
5482
|
-
}
|
|
5560
|
+
this.currentTableNodeKey = null;
|
|
5561
|
+
this.currentCellKey = null;
|
|
5483
5562
|
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
if (!cell) return
|
|
5563
|
+
this.#registerKeyHandlers();
|
|
5564
|
+
}
|
|
5487
5565
|
|
|
5488
|
-
|
|
5489
|
-
|
|
5566
|
+
destroy() {
|
|
5567
|
+
this.currentTableNodeKey = null;
|
|
5568
|
+
this.currentCellKey = null;
|
|
5490
5569
|
|
|
5491
|
-
|
|
5570
|
+
this.#unregisterKeyHandlers();
|
|
5492
5571
|
}
|
|
5493
5572
|
|
|
5494
|
-
|
|
5495
|
-
this
|
|
5496
|
-
cell.classList.remove(theme.tableCellFocus);
|
|
5497
|
-
});
|
|
5498
|
-
|
|
5499
|
-
this.#editorElement.querySelectorAll(`.${theme.tableCellHighlight}`)?.forEach(cell => {
|
|
5500
|
-
cell.classList.remove(theme.tableCellHighlight);
|
|
5501
|
-
cell.removeAttribute("data-action");
|
|
5502
|
-
cell.removeAttribute("data-child-type");
|
|
5503
|
-
cell.removeAttribute("data-direction");
|
|
5504
|
-
});
|
|
5573
|
+
get currentCell() {
|
|
5574
|
+
if (!this.currentCellKey) return null
|
|
5505
5575
|
|
|
5506
|
-
this
|
|
5576
|
+
return this.editor.getEditorState().read(() => {
|
|
5577
|
+
const cell = $getNodeByKey(this.currentCellKey);
|
|
5578
|
+
return (cell instanceof TableCellNode) ? cell : null
|
|
5579
|
+
})
|
|
5507
5580
|
}
|
|
5508
5581
|
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5582
|
+
get currentTableNode() {
|
|
5583
|
+
if (!this.currentTableNodeKey) return null
|
|
5584
|
+
|
|
5585
|
+
return this.editor.getEditorState().read(() => {
|
|
5586
|
+
const tableNode = $getNodeByKey(this.currentTableNodeKey);
|
|
5587
|
+
return (tableNode instanceof TableNode) ? tableNode : null
|
|
5588
|
+
})
|
|
5514
5589
|
}
|
|
5515
|
-
}
|
|
5516
5590
|
|
|
5517
|
-
|
|
5591
|
+
get currentRowCells() {
|
|
5592
|
+
const currentRowIndex = this.currentRowIndex;
|
|
5518
5593
|
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
async buildListItems(filter = "") {
|
|
5522
|
-
return Promise.resolve([])
|
|
5523
|
-
}
|
|
5594
|
+
const rows = this.tableRows;
|
|
5595
|
+
if (!rows) return null
|
|
5524
5596
|
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5597
|
+
return this.editor.getEditorState().read(() => {
|
|
5598
|
+
return rows[currentRowIndex]?.getChildren() ?? null
|
|
5599
|
+
}) ?? null
|
|
5528
5600
|
}
|
|
5529
5601
|
|
|
5530
|
-
|
|
5602
|
+
get currentRowIndex() {
|
|
5603
|
+
const currentCell = this.currentCell;
|
|
5604
|
+
if (!currentCell) return 0
|
|
5531
5605
|
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
const listItemElement = createElement("li", { role: "option", id: generateDomId("prompt-item"), tabindex: "0" });
|
|
5536
|
-
listItemElement.classList.add("lexxy-prompt-menu__item");
|
|
5537
|
-
listItemElement.appendChild(fragment);
|
|
5538
|
-
return listItemElement
|
|
5606
|
+
return this.editor.getEditorState().read(() => {
|
|
5607
|
+
return $getTableRowIndexFromTableCellNode(currentCell)
|
|
5608
|
+
}) ?? 0
|
|
5539
5609
|
}
|
|
5540
5610
|
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
const response = await fetch(url);
|
|
5544
|
-
const html = await response.text();
|
|
5545
|
-
const promptItems = parseHtml(html).querySelectorAll("lexxy-prompt-item");
|
|
5546
|
-
return Promise.resolve(Array.from(promptItems))
|
|
5547
|
-
} catch (error) {
|
|
5548
|
-
return Promise.reject(error)
|
|
5549
|
-
}
|
|
5550
|
-
}
|
|
5551
|
-
}
|
|
5611
|
+
get currentColumnCells() {
|
|
5612
|
+
const columnIndex = this.currentColumnIndex;
|
|
5552
5613
|
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
return this
|
|
5614
|
+
const rows = this.tableRows;
|
|
5615
|
+
if (!rows) return null
|
|
5616
|
+
|
|
5617
|
+
return this.editor.getEditorState().read(() => {
|
|
5618
|
+
return rows.map(row => row.getChildAtIndex(columnIndex))
|
|
5619
|
+
}) ?? null
|
|
5557
5620
|
}
|
|
5558
5621
|
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5622
|
+
get currentColumnIndex() {
|
|
5623
|
+
const currentCell = this.currentCell;
|
|
5624
|
+
if (!currentCell) return 0
|
|
5625
|
+
|
|
5626
|
+
return this.editor.getEditorState().read(() => {
|
|
5627
|
+
return $getTableColumnIndexFromTableCellNode(currentCell)
|
|
5628
|
+
}) ?? 0
|
|
5562
5629
|
}
|
|
5563
5630
|
|
|
5564
|
-
|
|
5565
|
-
return this.
|
|
5631
|
+
get tableRows() {
|
|
5632
|
+
return this.editor.getEditorState().read(() => {
|
|
5633
|
+
return this.currentTableNode?.getChildren()
|
|
5634
|
+
}) ?? null
|
|
5566
5635
|
}
|
|
5567
5636
|
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
promptItems.forEach((promptItem) => {
|
|
5572
|
-
const searchableText = promptItem.getAttribute("search");
|
|
5637
|
+
updateSelectedTable() {
|
|
5638
|
+
let cellNode = null;
|
|
5639
|
+
let tableNode = null;
|
|
5573
5640
|
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5641
|
+
this.editor.getEditorState().read(() => {
|
|
5642
|
+
const selection = $getSelection();
|
|
5643
|
+
if (!selection || !this.selection.isTableCellSelected) return
|
|
5644
|
+
|
|
5645
|
+
const node = selection.getNodes()[0];
|
|
5646
|
+
|
|
5647
|
+
cellNode = $findCellNode(node);
|
|
5648
|
+
tableNode = $findTableNode(node);
|
|
5579
5649
|
});
|
|
5580
5650
|
|
|
5581
|
-
|
|
5651
|
+
this.currentCellKey = cellNode?.getKey() ?? null;
|
|
5652
|
+
this.currentTableNodeKey = tableNode?.getKey() ?? null;
|
|
5582
5653
|
}
|
|
5583
|
-
}
|
|
5584
5654
|
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5655
|
+
executeTableCommand(command, customIndex = null) {
|
|
5656
|
+
if (command.action === "delete" && command.childType === "table") {
|
|
5657
|
+
this.#deleteTable();
|
|
5658
|
+
return
|
|
5659
|
+
}
|
|
5660
|
+
|
|
5661
|
+
if (command.action === "toggle") {
|
|
5662
|
+
this.#executeToggleStyle(command);
|
|
5663
|
+
return
|
|
5664
|
+
}
|
|
5665
|
+
|
|
5666
|
+
this.#executeCommand(command, customIndex);
|
|
5589
5667
|
}
|
|
5590
5668
|
|
|
5591
|
-
|
|
5592
|
-
|
|
5669
|
+
#executeCommand(command, customIndex = null) {
|
|
5670
|
+
this.#selectCellAtSelection();
|
|
5671
|
+
this.editor.dispatchCommand(this.#commandName(command));
|
|
5672
|
+
this.#selectNextBestCell(command, customIndex);
|
|
5593
5673
|
}
|
|
5594
|
-
}
|
|
5595
5674
|
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
super();
|
|
5599
|
-
this.url = url;
|
|
5675
|
+
#executeToggleStyle(command) {
|
|
5676
|
+
const childType = command.childType;
|
|
5600
5677
|
|
|
5601
|
-
|
|
5602
|
-
|
|
5678
|
+
let cells = null;
|
|
5679
|
+
let headerState = null;
|
|
5603
5680
|
|
|
5604
|
-
|
|
5605
|
-
|
|
5681
|
+
if (childType === "row") {
|
|
5682
|
+
cells = this.currentRowCells;
|
|
5683
|
+
headerState = TableCellHeaderStates.ROW;
|
|
5684
|
+
} else if (childType === "column") {
|
|
5685
|
+
cells = this.currentColumnCells;
|
|
5686
|
+
headerState = TableCellHeaderStates.COLUMN;
|
|
5687
|
+
}
|
|
5606
5688
|
|
|
5607
|
-
|
|
5608
|
-
}
|
|
5609
|
-
}
|
|
5689
|
+
if (!cells || cells.length === 0) return
|
|
5610
5690
|
|
|
5611
|
-
|
|
5691
|
+
this.editor.update(() => {
|
|
5692
|
+
const firstCell = $getTableCellNodeFromLexicalNode(cells[0]);
|
|
5693
|
+
if (!firstCell) return
|
|
5612
5694
|
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
super();
|
|
5695
|
+
const currentStyle = firstCell.getHeaderStyles();
|
|
5696
|
+
const newStyle = currentStyle ^ headerState;
|
|
5616
5697
|
|
|
5617
|
-
|
|
5618
|
-
|
|
5698
|
+
cells.forEach(cell => {
|
|
5699
|
+
this.#setHeaderStyle(cell, newStyle, headerState);
|
|
5700
|
+
});
|
|
5701
|
+
});
|
|
5619
5702
|
}
|
|
5620
5703
|
|
|
5621
|
-
|
|
5622
|
-
|
|
5704
|
+
#deleteTable() {
|
|
5705
|
+
this.#selectCellAtSelection();
|
|
5706
|
+
this.editor.dispatchCommand("deleteTable");
|
|
5623
5707
|
}
|
|
5624
5708
|
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5709
|
+
#selectCellAtSelection() {
|
|
5710
|
+
this.editor.update(() => {
|
|
5711
|
+
const selection = $getSelection();
|
|
5712
|
+
if (!selection) return
|
|
5628
5713
|
|
|
5629
|
-
|
|
5630
|
-
const promptItems = await this.loadPromptItemsFromUrl(this.#urlFor(filter));
|
|
5631
|
-
return this.#buildListItemsFromPromptItems(promptItems)
|
|
5632
|
-
}
|
|
5714
|
+
const node = selection.getNodes()[0];
|
|
5633
5715
|
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
url.searchParams.append("filter", filter);
|
|
5637
|
-
return url.toString()
|
|
5716
|
+
$findCellNode(node)?.selectEnd();
|
|
5717
|
+
});
|
|
5638
5718
|
}
|
|
5639
5719
|
|
|
5640
|
-
#
|
|
5641
|
-
const
|
|
5642
|
-
this.promptItemByListItem = new WeakMap();
|
|
5643
|
-
|
|
5644
|
-
for (const promptItem of promptItems) {
|
|
5645
|
-
const listItem = this.buildListItemElementFor(promptItem);
|
|
5646
|
-
this.promptItemByListItem.set(listItem, promptItem);
|
|
5647
|
-
listItems.push(listItem);
|
|
5648
|
-
}
|
|
5720
|
+
#commandName(command) {
|
|
5721
|
+
const { action, childType, direction } = command;
|
|
5649
5722
|
|
|
5650
|
-
|
|
5723
|
+
const childTypeSuffix = upcaseFirst(childType);
|
|
5724
|
+
const directionSuffix = action == "insert" ? upcaseFirst(direction) : "";
|
|
5725
|
+
return `${action}Table${childTypeSuffix}${directionSuffix}`
|
|
5651
5726
|
}
|
|
5652
|
-
}
|
|
5653
|
-
|
|
5654
|
-
const NOTHING_FOUND_DEFAULT_MESSAGE = "Nothing found";
|
|
5655
5727
|
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
this.keyListeners = [];
|
|
5728
|
+
#setHeaderStyle(cell, newStyle, headerState) {
|
|
5729
|
+
const tableCellNode = $getTableCellNodeFromLexicalNode(cell);
|
|
5730
|
+
tableCellNode?.setHeaderStyles(newStyle, headerState);
|
|
5660
5731
|
}
|
|
5661
5732
|
|
|
5662
|
-
|
|
5733
|
+
async #selectCellAtIndex(rowIndex, columnIndex) {
|
|
5734
|
+
// We wait for next frame, otherwise table operations might not have completed yet.
|
|
5735
|
+
await nextFrame();
|
|
5663
5736
|
|
|
5664
|
-
|
|
5665
|
-
this.source = this.#createSource();
|
|
5737
|
+
if (!this.currentTableNode) return
|
|
5666
5738
|
|
|
5667
|
-
this
|
|
5668
|
-
|
|
5669
|
-
}
|
|
5739
|
+
const rows = this.tableRows;
|
|
5740
|
+
if (!rows) return
|
|
5670
5741
|
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5742
|
+
const row = rows[rowIndex];
|
|
5743
|
+
if (!row) return
|
|
5744
|
+
|
|
5745
|
+
this.editor.update(() => {
|
|
5746
|
+
const cell = $getTableCellNodeFromLexicalNode(row.getChildAtIndex(columnIndex));
|
|
5747
|
+
cell?.selectEnd();
|
|
5748
|
+
});
|
|
5674
5749
|
}
|
|
5675
5750
|
|
|
5751
|
+
#selectNextBestCell(command, customIndex = null) {
|
|
5752
|
+
const { childType, direction } = command;
|
|
5676
5753
|
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5754
|
+
let rowIndex = this.currentRowIndex;
|
|
5755
|
+
let columnIndex = customIndex !== null ? customIndex : this.currentColumnIndex;
|
|
5756
|
+
|
|
5757
|
+
const deleteOffset = command.action === "delete" ? -1 : 0;
|
|
5758
|
+
const offset = direction === "after" ? 1 : deleteOffset;
|
|
5759
|
+
|
|
5760
|
+
if (childType === "row") {
|
|
5761
|
+
rowIndex += offset;
|
|
5762
|
+
} else if (childType === "column") {
|
|
5763
|
+
columnIndex += offset;
|
|
5680
5764
|
}
|
|
5681
|
-
}
|
|
5682
5765
|
|
|
5683
|
-
|
|
5684
|
-
return this.getAttribute("name")
|
|
5766
|
+
this.#selectCellAtIndex(rowIndex, columnIndex);
|
|
5685
5767
|
}
|
|
5686
5768
|
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5769
|
+
#selectNextRow() {
|
|
5770
|
+
const rows = this.tableRows;
|
|
5771
|
+
if (!rows) return
|
|
5690
5772
|
|
|
5691
|
-
|
|
5692
|
-
|
|
5773
|
+
const nextRow = rows.at(this.currentRowIndex + 1);
|
|
5774
|
+
if (!nextRow) return
|
|
5775
|
+
|
|
5776
|
+
this.editor.update(() => {
|
|
5777
|
+
nextRow.getChildAtIndex(this.currentColumnIndex)?.selectEnd();
|
|
5778
|
+
});
|
|
5693
5779
|
}
|
|
5694
5780
|
|
|
5695
|
-
|
|
5696
|
-
|
|
5781
|
+
#selectPreviousCell() {
|
|
5782
|
+
const cell = this.currentCell;
|
|
5783
|
+
if (!cell) return
|
|
5784
|
+
|
|
5785
|
+
this.editor.update(() => {
|
|
5786
|
+
cell.selectPrevious();
|
|
5787
|
+
});
|
|
5697
5788
|
}
|
|
5698
5789
|
|
|
5699
|
-
|
|
5700
|
-
|
|
5790
|
+
#insertRowAndSelectFirstCell() {
|
|
5791
|
+
this.executeTableCommand({ action: "insert", childType: "row", direction: "after" }, 0);
|
|
5701
5792
|
}
|
|
5702
5793
|
|
|
5703
|
-
|
|
5704
|
-
|
|
5794
|
+
#deleteRowAndSelectLastCell() {
|
|
5795
|
+
this.executeTableCommand({ action: "delete", childType: "row" }, -1);
|
|
5705
5796
|
}
|
|
5706
5797
|
|
|
5707
|
-
#
|
|
5708
|
-
const
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
5798
|
+
#deleteRowAndSelectNextNode() {
|
|
5799
|
+
const tableNode = this.currentTableNode;
|
|
5800
|
+
this.executeTableCommand({ action: "delete", childType: "row" });
|
|
5801
|
+
|
|
5802
|
+
this.editor.update(() => {
|
|
5803
|
+
const next = tableNode?.getNextSibling();
|
|
5804
|
+
if ($isParagraphNode(next)) {
|
|
5805
|
+
next.selectStart();
|
|
5712
5806
|
} else {
|
|
5713
|
-
|
|
5807
|
+
const newParagraph = $createParagraphNode();
|
|
5808
|
+
this.currentTableNode.insertAfter(newParagraph);
|
|
5809
|
+
newParagraph.selectStart();
|
|
5714
5810
|
}
|
|
5715
|
-
}
|
|
5716
|
-
return new InlinePromptSource(this.querySelectorAll("lexxy-prompt-item"))
|
|
5717
|
-
}
|
|
5811
|
+
});
|
|
5718
5812
|
}
|
|
5719
5813
|
|
|
5720
|
-
#
|
|
5721
|
-
|
|
5722
|
-
editorState.read(() => {
|
|
5723
|
-
const { node, offset } = this.#selection.selectedNodeWithOffset();
|
|
5724
|
-
if (!node) return
|
|
5814
|
+
#isCurrentCellEmpty() {
|
|
5815
|
+
if (!this.currentTableNode) return false
|
|
5725
5816
|
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
const triggerLength = this.trigger.length;
|
|
5817
|
+
const cell = this.currentCell;
|
|
5818
|
+
if (!cell) return false
|
|
5729
5819
|
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
const textBeforeCursor = fullText.slice(offset - triggerLength, offset);
|
|
5820
|
+
return cell.getTextContent().trim() === ""
|
|
5821
|
+
}
|
|
5733
5822
|
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
const isAtStart = offset === triggerLength;
|
|
5823
|
+
#isCurrentRowLast() {
|
|
5824
|
+
if (!this.currentTableNode) return false
|
|
5737
5825
|
|
|
5738
|
-
|
|
5739
|
-
|
|
5826
|
+
const rows = this.tableRows;
|
|
5827
|
+
if (!rows) return false
|
|
5740
5828
|
|
|
5741
|
-
|
|
5742
|
-
unregister();
|
|
5743
|
-
this.#showPopover();
|
|
5744
|
-
}
|
|
5745
|
-
}
|
|
5746
|
-
}
|
|
5747
|
-
}
|
|
5748
|
-
});
|
|
5749
|
-
});
|
|
5829
|
+
return rows.length === this.currentRowIndex + 1
|
|
5750
5830
|
}
|
|
5751
5831
|
|
|
5752
|
-
#
|
|
5753
|
-
|
|
5754
|
-
if (this.closed) return
|
|
5755
|
-
|
|
5756
|
-
this.#editor.read(() => {
|
|
5757
|
-
const { node, offset } = this.#selection.selectedNodeWithOffset();
|
|
5758
|
-
if (!node) return
|
|
5832
|
+
#isCurrentRowEmpty() {
|
|
5833
|
+
if (!this.currentTableNode) return false
|
|
5759
5834
|
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
const textBeforeCursor = fullText.slice(0, offset);
|
|
5763
|
-
const lastTriggerIndex = textBeforeCursor.lastIndexOf(this.trigger);
|
|
5764
|
-
const triggerEndIndex = lastTriggerIndex + this.trigger.length - 1;
|
|
5835
|
+
const cells = this.currentRowCells;
|
|
5836
|
+
if (!cells) return false
|
|
5765
5837
|
|
|
5766
|
-
|
|
5767
|
-
if (lastTriggerIndex === -1 || offset <= triggerEndIndex) {
|
|
5768
|
-
this.#hidePopover();
|
|
5769
|
-
}
|
|
5770
|
-
} else {
|
|
5771
|
-
// Cursor is not in a text node or at offset 0, hide popover
|
|
5772
|
-
this.#hidePopover();
|
|
5773
|
-
}
|
|
5774
|
-
});
|
|
5775
|
-
});
|
|
5838
|
+
return cells.every(cell => cell.getTextContent().trim() === "")
|
|
5776
5839
|
}
|
|
5777
5840
|
|
|
5778
|
-
#
|
|
5779
|
-
if (this.
|
|
5780
|
-
this.cursorPositionListener();
|
|
5781
|
-
this.cursorPositionListener = null;
|
|
5782
|
-
}
|
|
5783
|
-
}
|
|
5841
|
+
#isFirstCellInRow() {
|
|
5842
|
+
if (!this.currentTableNode) return false
|
|
5784
5843
|
|
|
5785
|
-
|
|
5786
|
-
return
|
|
5787
|
-
}
|
|
5844
|
+
const cells = this.currentRowCells;
|
|
5845
|
+
if (!cells) return false
|
|
5788
5846
|
|
|
5789
|
-
|
|
5790
|
-
return this.closest("lexxy-editor")
|
|
5847
|
+
return cells.indexOf(this.currentCell) === 0
|
|
5791
5848
|
}
|
|
5792
5849
|
|
|
5793
|
-
|
|
5794
|
-
|
|
5850
|
+
#registerKeyHandlers() {
|
|
5851
|
+
// We can't prevent these externally using regular keydown because Lexical handles it first.
|
|
5852
|
+
this.unregisterBackspaceKeyHandler = this.editor.registerCommand(KEY_BACKSPACE_COMMAND, (event) => this.#handleBackspaceKey(event), COMMAND_PRIORITY_HIGH);
|
|
5853
|
+
this.unregisterEnterKeyHandler = this.editor.registerCommand(KEY_ENTER_COMMAND, (event) => this.#handleEnterKey(event), COMMAND_PRIORITY_HIGH);
|
|
5795
5854
|
}
|
|
5796
5855
|
|
|
5797
|
-
|
|
5798
|
-
this.
|
|
5799
|
-
this
|
|
5800
|
-
await this.#filterOptions();
|
|
5801
|
-
this.popoverElement.classList.toggle("lexxy-prompt-menu--visible", true);
|
|
5802
|
-
this.#selectFirstOption();
|
|
5803
|
-
|
|
5804
|
-
this.#editorElement.addEventListener("keydown", this.#handleKeydownOnPopover);
|
|
5805
|
-
this.#editorElement.addEventListener("lexxy:change", this.#filterOptions);
|
|
5856
|
+
#unregisterKeyHandlers() {
|
|
5857
|
+
this.unregisterBackspaceKeyHandler?.();
|
|
5858
|
+
this.unregisterEnterKeyHandler?.();
|
|
5806
5859
|
|
|
5807
|
-
this
|
|
5808
|
-
this
|
|
5860
|
+
this.unregisterBackspaceKeyHandler = null;
|
|
5861
|
+
this.unregisterEnterKeyHandler = null;
|
|
5809
5862
|
}
|
|
5810
5863
|
|
|
5811
|
-
#
|
|
5812
|
-
|
|
5813
|
-
this.keyListeners.push(this.#editor.registerCommand(KEY_ENTER_COMMAND, this.#handleSelectedOption.bind(this), COMMAND_PRIORITY_HIGH));
|
|
5814
|
-
this.keyListeners.push(this.#editor.registerCommand(KEY_TAB_COMMAND, this.#handleSelectedOption.bind(this), COMMAND_PRIORITY_HIGH));
|
|
5864
|
+
#handleBackspaceKey(event) {
|
|
5865
|
+
if (!this.currentTableNode) return false
|
|
5815
5866
|
|
|
5816
|
-
if (this.#
|
|
5817
|
-
|
|
5867
|
+
if (this.#isCurrentRowEmpty() && this.#isFirstCellInRow()) {
|
|
5868
|
+
event.preventDefault();
|
|
5869
|
+
this.#deleteRowAndSelectLastCell();
|
|
5870
|
+
return true
|
|
5818
5871
|
}
|
|
5819
5872
|
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5873
|
+
if (this.#isCurrentCellEmpty() && !this.#isFirstCellInRow()) {
|
|
5874
|
+
event.preventDefault();
|
|
5875
|
+
this.#selectPreviousCell();
|
|
5876
|
+
return true
|
|
5877
|
+
}
|
|
5824
5878
|
|
|
5825
|
-
|
|
5826
|
-
this.#moveSelectionUp();
|
|
5827
|
-
event.preventDefault();
|
|
5828
|
-
return true
|
|
5879
|
+
return false
|
|
5829
5880
|
}
|
|
5830
5881
|
|
|
5831
|
-
#
|
|
5832
|
-
|
|
5882
|
+
#handleEnterKey(event) {
|
|
5883
|
+
if ((event.ctrlKey || event.metaKey) || event.shiftKey || !this.currentTableNode) return false
|
|
5884
|
+
|
|
5885
|
+
if (this.selection.isInsideList || this.selection.isInsideCodeBlock) return false
|
|
5886
|
+
|
|
5833
5887
|
event.preventDefault();
|
|
5888
|
+
|
|
5889
|
+
if (this.#isCurrentRowLast() && this.#isCurrentRowEmpty()) {
|
|
5890
|
+
this.#deleteRowAndSelectNextNode();
|
|
5891
|
+
} else if (this.#isCurrentRowLast()) {
|
|
5892
|
+
this.#insertRowAndSelectFirstCell();
|
|
5893
|
+
} else {
|
|
5894
|
+
this.#selectNextRow();
|
|
5895
|
+
}
|
|
5896
|
+
|
|
5834
5897
|
return true
|
|
5835
5898
|
}
|
|
5899
|
+
}
|
|
5836
5900
|
|
|
5837
|
-
|
|
5838
|
-
|
|
5901
|
+
var TableIcons = {
|
|
5902
|
+
"insert-row-before":
|
|
5903
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5904
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.86804e-07 15C8.29055e-07 15.8284 0.671574 16.5 1.5 16.5H15L15.1533 16.4922C15.8593 16.4205 16.4205 15.8593 16.4922 15.1533L16.5 15V4.5L16.4922 4.34668C16.4154 3.59028 15.7767 3 15 3H13.5L13.5 4.5H15V9H1.5L1.5 4.5L3 4.5V3H1.5C0.671574 3 1.20956e-06 3.67157 1.24577e-06 4.5L7.86804e-07 15ZM15 10.5V15H1.5L1.5 10.5H15Z"/>
|
|
5905
|
+
<path d="M4.5 4.5H7.5V7.5H9V4.5H12L12 3L9 3V6.55671e-08L7.5 0V3L4.5 3V4.5Z"/>
|
|
5906
|
+
</svg>`,
|
|
5839
5907
|
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5908
|
+
"insert-row-after":
|
|
5909
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5910
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.86804e-07 13.5C7.50592e-07 14.3284 0.671574 15 1.5 15H3V13.5H1.5L1.5 9L15 9V13.5H13.5V15H15C15.7767 15 16.4154 14.4097 16.4922 13.6533L16.5 13.5V3L16.4922 2.84668C16.4205 2.14069 15.8593 1.57949 15.1533 1.50781L15 1.5L1.5 1.5C0.671574 1.5 1.28803e-06 2.17157 1.24577e-06 3L7.86804e-07 13.5ZM15 3V7.5L1.5 7.5L1.5 3L15 3Z"/>
|
|
5911
|
+
<path d="M7.5 15V18H9V15H12V13.5H9V10.5H7.5V13.5H4.5V15H7.5Z"/>
|
|
5912
|
+
</svg>`,
|
|
5913
|
+
|
|
5914
|
+
"delete-row":
|
|
5915
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5916
|
+
<path d="M16.4922 12.1533C16.4154 12.9097 15.7767 13.5 15 13.5L12 13.5V12H15V6L1.5 6L1.5 12H4.5V13.5H1.5C0.723337 13.5 0.0846104 12.9097 0.00781328 12.1533L7.86804e-07 12L1.04907e-06 6C1.17362e-06 5.22334 0.590278 4.58461 1.34668 4.50781L1.5 4.5L15 4.5C15.8284 4.5 16.5 5.17157 16.5 6V12L16.4922 12.1533Z"/>
|
|
5917
|
+
<path d="M10.3711 15.9316L8.25 13.8096L6.12793 15.9316L5.06738 14.8711L7.18945 12.75L5.06738 10.6289L6.12793 9.56836L8.25 11.6895L10.3711 9.56836L11.4316 10.6289L9.31055 12.75L11.4316 14.8711L10.3711 15.9316Z"/>
|
|
5918
|
+
</svg>`,
|
|
5919
|
+
|
|
5920
|
+
"toggle-row":
|
|
5921
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5922
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.00781328 13.6533C0.0846108 14.4097 0.723337 15 1.5 15L15 15L15.1533 14.9922C15.8593 14.9205 16.4205 14.3593 16.4922 13.6533L16.5 13.5V4.5L16.4922 4.34668C16.4205 3.64069 15.8593 3.07949 15.1533 3.00781L15 3L1.5 3C0.671574 3 1.24863e-06 3.67157 1.18021e-06 4.5L7.86804e-07 13.5L0.00781328 13.6533ZM15 9V13.5L1.5 13.5L1.5 9L15 9Z"/>
|
|
5923
|
+
</svg>`,
|
|
5924
|
+
|
|
5925
|
+
"insert-column-before":
|
|
5926
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5927
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.5 0C3.67157 0 3 0.671573 3 1.5V3H4.5V1.5H9V15H4.5V13.5H3V15C3 15.7767 3.59028 16.4154 4.34668 16.4922L4.5 16.5H15L15.1533 16.4922C15.8593 16.4205 16.4205 15.8593 16.4922 15.1533L16.5 15V1.5C16.5 0.671573 15.8284 6.03989e-09 15 0H4.5ZM15 15H10.5V1.5H15V15Z"/>
|
|
5928
|
+
<path d="M3 7.5H0V9H3V12H4.5V9H7.5V7.5H4.5V4.5H3V7.5Z"/>
|
|
5929
|
+
</svg>`,
|
|
5930
|
+
|
|
5931
|
+
"insert-column-after":
|
|
5932
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5933
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.5 0C14.3284 0 15 0.671573 15 1.5V3H13.5V1.5H9V15H13.5V13.5H15V15C15 15.7767 14.4097 16.4154 13.6533 16.4922L13.5 16.5H3L2.84668 16.4922C2.14069 16.4205 1.57949 15.8593 1.50781 15.1533L1.5 15V1.5C1.5 0.671573 2.17157 6.03989e-09 3 0H13.5ZM3 15H7.5V1.5H3V15Z"/>
|
|
5934
|
+
<path d="M15 7.5H18V9H15V12H13.5V9H10.5V7.5H13.5V4.5H15V7.5Z"/>
|
|
5935
|
+
</svg>`,
|
|
5844
5936
|
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5937
|
+
"delete-column":
|
|
5938
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5939
|
+
<path d="M12.1533 0.0078125C12.9097 0.0846097 13.5 0.723336 13.5 1.5V4.5H12V1.5H6V15H12V12H13.5V15C13.5 15.7767 12.9097 16.4154 12.1533 16.4922L12 16.5H6C5.22334 16.5 4.58461 15.9097 4.50781 15.1533L4.5 15V1.5C4.5 0.671573 5.17157 2.41596e-08 6 0H12L12.1533 0.0078125Z"/>
|
|
5940
|
+
<path d="M15.9316 6.12891L13.8105 8.24902L15.9326 10.3711L14.8711 11.4316L12.75 9.31055L10.6289 11.4316L9.56738 10.3711L11.6885 8.24902L9.56836 6.12891L10.6289 5.06836L12.75 7.18848L14.8711 5.06836L15.9316 6.12891Z"/>
|
|
5941
|
+
</svg>`,
|
|
5848
5942
|
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
listItem.focus();
|
|
5943
|
+
"toggle-column":
|
|
5944
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5945
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.6533 17.9922C14.4097 17.9154 15 17.2767 15 16.5L15 3L14.9922 2.84668C14.9205 2.14069 14.3593 1.57949 13.6533 1.50781L13.5 1.5L4.5 1.5L4.34668 1.50781C3.59028 1.58461 3 2.22334 3 3L3 16.5C3 17.2767 3.59028 17.9154 4.34668 17.9922L4.5 18L13.5 18L13.6533 17.9922ZM9 3L13.5 3L13.5 16.5L9 16.5L9 3Z" />
|
|
5946
|
+
</svg>`,
|
|
5854
5947
|
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5948
|
+
"delete-table":
|
|
5949
|
+
`<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
5950
|
+
<path d="M18.2129 19.2305C18.0925 20.7933 16.7892 22 15.2217 22H7.77832C6.21084 22 4.90753 20.7933 4.78711 19.2305L4 9H19L18.2129 19.2305Z"/><path d="M13 2C14.1046 2 15 2.89543 15 4H19C19.5523 4 20 4.44772 20 5V6C20 6.55228 19.5523 7 19 7H4C3.44772 7 3 6.55228 3 6V5C3 4.44772 3.44772 4 4 4H8C8 2.89543 8.89543 2 10 2H13Z"/>
|
|
5951
|
+
</svg>`
|
|
5952
|
+
};
|
|
5859
5953
|
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
this
|
|
5863
|
-
}
|
|
5954
|
+
class TableTools extends HTMLElement {
|
|
5955
|
+
connectedCallback() {
|
|
5956
|
+
this.tableController = new TableController(this.#editorElement);
|
|
5864
5957
|
|
|
5865
|
-
|
|
5866
|
-
this.#
|
|
5867
|
-
this.#
|
|
5868
|
-
this.#editorContentElement.removeAttribute("aria-activedescendant");
|
|
5869
|
-
this.#editorContentElement.removeAttribute("aria-haspopup");
|
|
5958
|
+
this.#setUpButtons();
|
|
5959
|
+
this.#monitorForTableSelection();
|
|
5960
|
+
this.#registerKeyboardShortcuts();
|
|
5870
5961
|
}
|
|
5871
5962
|
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
const editorRect = this.#editorElement.getBoundingClientRect();
|
|
5875
|
-
const contentRect = this.#editorContentElement.getBoundingClientRect();
|
|
5876
|
-
const verticalOffset = contentRect.top - editorRect.top;
|
|
5877
|
-
|
|
5878
|
-
if (!this.popoverElement.hasAttribute("data-anchored")) {
|
|
5879
|
-
this.popoverElement.style.left = `${x}px`;
|
|
5880
|
-
this.popoverElement.toggleAttribute("data-anchored", true);
|
|
5881
|
-
}
|
|
5963
|
+
disconnectedCallback() {
|
|
5964
|
+
this.#unregisterKeyboardShortcuts();
|
|
5882
5965
|
|
|
5883
|
-
this.
|
|
5884
|
-
this.
|
|
5966
|
+
this.unregisterUpdateListener?.();
|
|
5967
|
+
this.unregisterUpdateListener = null;
|
|
5885
5968
|
|
|
5886
|
-
|
|
5887
|
-
const isClippedAtBottom = popoverRect.bottom > window.innerHeight;
|
|
5969
|
+
this.removeEventListener("keydown", this.#handleToolsKeydown);
|
|
5888
5970
|
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
this.popoverElement.style.bottom = "auto";
|
|
5892
|
-
this.popoverElement.toggleAttribute("data-clipped-at-bottom", true);
|
|
5893
|
-
}
|
|
5971
|
+
this.tableController?.destroy();
|
|
5972
|
+
this.tableController = null;
|
|
5894
5973
|
}
|
|
5895
5974
|
|
|
5896
|
-
#
|
|
5897
|
-
this.
|
|
5898
|
-
this.popoverElement.removeAttribute("data-anchored");
|
|
5975
|
+
get #editor() {
|
|
5976
|
+
return this.#editorElement.editor
|
|
5899
5977
|
}
|
|
5900
5978
|
|
|
5901
|
-
|
|
5902
|
-
this
|
|
5903
|
-
this.popoverElement.classList.toggle("lexxy-prompt-menu--visible", false);
|
|
5904
|
-
this.#editorElement.removeEventListener("lexxy:change", this.#filterOptions);
|
|
5905
|
-
this.#editorElement.removeEventListener("keydown", this.#handleKeydownOnPopover);
|
|
5906
|
-
|
|
5907
|
-
this.#unregisterKeyListeners();
|
|
5908
|
-
this.#removeCursorPositionListener();
|
|
5909
|
-
|
|
5910
|
-
await nextFrame();
|
|
5911
|
-
this.#addTriggerListener();
|
|
5979
|
+
get #editorElement() {
|
|
5980
|
+
return this.closest("lexxy-editor")
|
|
5912
5981
|
}
|
|
5913
5982
|
|
|
5914
|
-
#
|
|
5915
|
-
this.
|
|
5916
|
-
this.keyListeners = [];
|
|
5983
|
+
get #tableToolsButtons() {
|
|
5984
|
+
return Array.from(this.querySelectorAll("button, details > summary"))
|
|
5917
5985
|
}
|
|
5918
5986
|
|
|
5919
|
-
#
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
return
|
|
5923
|
-
}
|
|
5987
|
+
#setUpButtons() {
|
|
5988
|
+
this.appendChild(this.#createRowButtonsContainer());
|
|
5989
|
+
this.appendChild(this.#createColumnButtonsContainer());
|
|
5924
5990
|
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
await nextFrame();
|
|
5928
|
-
this.#positionPopover();
|
|
5929
|
-
} else {
|
|
5930
|
-
this.#hidePopover();
|
|
5931
|
-
}
|
|
5991
|
+
this.appendChild(this.#createDeleteTableButton());
|
|
5992
|
+
this.addEventListener("keydown", this.#handleToolsKeydown);
|
|
5932
5993
|
}
|
|
5933
5994
|
|
|
5934
|
-
|
|
5935
|
-
const
|
|
5936
|
-
const filteredListItems = await this.source.buildListItems(filter);
|
|
5937
|
-
this.popoverElement.innerHTML = "";
|
|
5995
|
+
#createButtonsContainer(childType, setCountProperty, moreMenu) {
|
|
5996
|
+
const container = createElement("div", { className: `lexxy-table-control lexxy-table-control--${childType}` });
|
|
5938
5997
|
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
} else {
|
|
5942
|
-
this.#showEmptyResults();
|
|
5943
|
-
}
|
|
5944
|
-
this.#selectFirstOption();
|
|
5945
|
-
}
|
|
5998
|
+
const plusButton = this.#createButton(`Add ${childType}`, { action: "insert", childType, direction: "after" }, "+");
|
|
5999
|
+
const minusButton = this.#createButton(`Remove ${childType}`, { action: "delete", childType }, "−");
|
|
5946
6000
|
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
}
|
|
6001
|
+
const dropdown = createElement("details", { className: "lexxy-table-control__more-menu" });
|
|
6002
|
+
dropdown.setAttribute("name", "lexxy-dropdown");
|
|
6003
|
+
dropdown.tabIndex = -1;
|
|
5951
6004
|
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
el.classList.add("lexxy-prompt-menu__item--empty");
|
|
5956
|
-
this.popoverElement.append(el);
|
|
5957
|
-
}
|
|
6005
|
+
const count = createElement("summary", {}, `_ ${childType}s`);
|
|
6006
|
+
setCountProperty(count);
|
|
6007
|
+
dropdown.appendChild(count);
|
|
5958
6008
|
|
|
5959
|
-
|
|
5960
|
-
return this.getAttribute("empty-results") || NOTHING_FOUND_DEFAULT_MESSAGE
|
|
5961
|
-
}
|
|
6009
|
+
dropdown.appendChild(moreMenu);
|
|
5962
6010
|
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
this.#editorElement.focus();
|
|
5967
|
-
event.stopPropagation();
|
|
5968
|
-
}
|
|
5969
|
-
// Arrow keys are now handled via Lexical commands with HIGH priority
|
|
5970
|
-
}
|
|
6011
|
+
container.appendChild(minusButton);
|
|
6012
|
+
container.appendChild(dropdown);
|
|
6013
|
+
container.appendChild(plusButton);
|
|
5971
6014
|
|
|
5972
|
-
|
|
5973
|
-
const nextIndex = this.#selectedIndex + 1;
|
|
5974
|
-
if (nextIndex < this.#listItemElements.length) this.#selectOption(this.#listItemElements[nextIndex]);
|
|
6015
|
+
return container
|
|
5975
6016
|
}
|
|
5976
6017
|
|
|
5977
|
-
#
|
|
5978
|
-
|
|
5979
|
-
|
|
6018
|
+
#createRowButtonsContainer() {
|
|
6019
|
+
return this.#createButtonsContainer(
|
|
6020
|
+
"row",
|
|
6021
|
+
(count) => { this.rowCount = count; },
|
|
6022
|
+
this.#createMoreMenuSection("row")
|
|
6023
|
+
)
|
|
5980
6024
|
}
|
|
5981
6025
|
|
|
5982
|
-
|
|
5983
|
-
return this.#
|
|
6026
|
+
#createColumnButtonsContainer() {
|
|
6027
|
+
return this.#createButtonsContainer(
|
|
6028
|
+
"column",
|
|
6029
|
+
(count) => { this.columnCount = count; },
|
|
6030
|
+
this.#createMoreMenuSection("column")
|
|
6031
|
+
)
|
|
5984
6032
|
}
|
|
5985
6033
|
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
6034
|
+
#createMoreMenuSection(childType) {
|
|
6035
|
+
const section = createElement("div", { className: "lexxy-table-control__more-menu-details" });
|
|
6036
|
+
const addBeforeButton = this.#createButton(`Add ${childType} before`, { action: "insert", childType, direction: "before" });
|
|
6037
|
+
const addAfterButton = this.#createButton(`Add ${childType} after`, { action: "insert", childType, direction: "after" });
|
|
6038
|
+
const toggleStyleButton = this.#createButton(`Toggle ${childType} style`, { action: "toggle", childType });
|
|
6039
|
+
const deleteButton = this.#createButton(`Remove ${childType}`, { action: "delete", childType });
|
|
5989
6040
|
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
return true
|
|
5995
|
-
}
|
|
6041
|
+
section.appendChild(addBeforeButton);
|
|
6042
|
+
section.appendChild(addAfterButton);
|
|
6043
|
+
section.appendChild(toggleStyleButton);
|
|
6044
|
+
section.appendChild(deleteButton);
|
|
5996
6045
|
|
|
5997
|
-
|
|
5998
|
-
this.#replaceTriggerWithSelectedItem();
|
|
5999
|
-
this.#hidePopover();
|
|
6000
|
-
this.#editorElement.focus();
|
|
6046
|
+
return section
|
|
6001
6047
|
}
|
|
6002
6048
|
|
|
6003
|
-
#
|
|
6004
|
-
const
|
|
6005
|
-
|
|
6006
|
-
if (!promptItem) { return }
|
|
6049
|
+
#createDeleteTableButton() {
|
|
6050
|
+
const container = createElement("div", { className: "lexxy-table-control" });
|
|
6007
6051
|
|
|
6008
|
-
const
|
|
6009
|
-
|
|
6052
|
+
const deleteTableButton = this.#createButton("Delete this table?", { action: "delete", childType: "table" });
|
|
6053
|
+
deleteTableButton.classList.add("lexxy-table-control__button--delete-table");
|
|
6010
6054
|
|
|
6011
|
-
|
|
6012
|
-
this.#insertTemplatesAsEditableText(templates, stringToReplace);
|
|
6013
|
-
} else {
|
|
6014
|
-
this.#insertTemplatesAsAttachments(templates, stringToReplace, promptItem.getAttribute("sgid"));
|
|
6015
|
-
}
|
|
6016
|
-
}
|
|
6055
|
+
container.appendChild(deleteTableButton);
|
|
6017
6056
|
|
|
6018
|
-
|
|
6019
|
-
this.#editor.update(() => {
|
|
6020
|
-
const nodes = templates.flatMap(template => this.#buildEditableTextNodes(template));
|
|
6021
|
-
this.#editorContents.replaceTextBackUntil(stringToReplace, nodes);
|
|
6022
|
-
});
|
|
6023
|
-
}
|
|
6057
|
+
this.deleteContainer = container;
|
|
6024
6058
|
|
|
6025
|
-
|
|
6026
|
-
return $generateNodesFromDOM(this.#editor, parseHtml(`${template.innerHTML}`))
|
|
6059
|
+
return container
|
|
6027
6060
|
}
|
|
6028
6061
|
|
|
6029
|
-
#
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
|
|
6062
|
+
#createButton(label, command = {}, icon = this.#icon(command)) {
|
|
6063
|
+
const button = createElement("button", {
|
|
6064
|
+
className: "lexxy-table-control__button",
|
|
6065
|
+
"aria-label": label,
|
|
6066
|
+
type: "button"
|
|
6034
6067
|
});
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
#buildAttachmentNodes(templates, fallbackSgid = null) {
|
|
6038
|
-
return templates.map(
|
|
6039
|
-
template => this.#buildAttachmentNode(
|
|
6040
|
-
template.innerHTML,
|
|
6041
|
-
template.getAttribute("content-type") || this.#defaultPromptContentType,
|
|
6042
|
-
template.getAttribute("sgid") || fallbackSgid
|
|
6043
|
-
))
|
|
6044
|
-
}
|
|
6045
|
-
|
|
6046
|
-
#getSpacerTextNode() {
|
|
6047
|
-
return $createTextNode(" ")
|
|
6048
|
-
}
|
|
6068
|
+
button.tabIndex = -1;
|
|
6069
|
+
button.innerHTML = `${icon} <span>${label}</span>`;
|
|
6049
6070
|
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
}
|
|
6071
|
+
button.dataset.action = command.action;
|
|
6072
|
+
button.dataset.childType = command.childType;
|
|
6073
|
+
button.dataset.direction = command.direction;
|
|
6054
6074
|
|
|
6055
|
-
|
|
6056
|
-
return new CustomActionTextAttachmentNode({ sgid, contentType, innerHtml })
|
|
6057
|
-
}
|
|
6075
|
+
button.addEventListener("click", () => this.#executeTableCommand(command));
|
|
6058
6076
|
|
|
6059
|
-
|
|
6060
|
-
|
|
6077
|
+
button.addEventListener("mouseover", () => this.#handleCommandButtonHover());
|
|
6078
|
+
button.addEventListener("focus", () => this.#handleCommandButtonHover());
|
|
6079
|
+
button.addEventListener("mouseout", () => this.#handleCommandButtonHover());
|
|
6080
|
+
|
|
6081
|
+
return button
|
|
6061
6082
|
}
|
|
6062
6083
|
|
|
6063
|
-
|
|
6064
|
-
|
|
6084
|
+
#registerKeyboardShortcuts() {
|
|
6085
|
+
this.unregisterKeyboardShortcuts = this.#editor.registerCommand(KEY_DOWN_COMMAND, this.#handleAccessibilityShortcutKey, COMMAND_PRIORITY_HIGH);
|
|
6065
6086
|
}
|
|
6066
6087
|
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
popoverContainer.style.position = "absolute";
|
|
6071
|
-
popoverContainer.setAttribute("nonce", getNonce());
|
|
6072
|
-
popoverContainer.append(...await this.source.buildListItems());
|
|
6073
|
-
popoverContainer.addEventListener("click", this.#handlePopoverClick);
|
|
6074
|
-
this.#editorElement.appendChild(popoverContainer);
|
|
6075
|
-
return popoverContainer
|
|
6088
|
+
#unregisterKeyboardShortcuts() {
|
|
6089
|
+
this.unregisterKeyboardShortcuts?.();
|
|
6090
|
+
this.unregisterKeyboardShortcuts = null;
|
|
6076
6091
|
}
|
|
6077
6092
|
|
|
6078
|
-
#
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
this.#optionWasSelected();
|
|
6093
|
+
#handleAccessibilityShortcutKey = (event) => {
|
|
6094
|
+
if ((event.ctrlKey || event.metaKey) && event.shiftKey && event.key === "F10") {
|
|
6095
|
+
const firstButton = this.querySelector("button, [tabindex]:not([tabindex='-1'])");
|
|
6096
|
+
firstButton?.focus();
|
|
6083
6097
|
}
|
|
6084
6098
|
}
|
|
6085
6099
|
|
|
6086
|
-
#
|
|
6087
|
-
|
|
6088
|
-
|
|
6100
|
+
#handleToolsKeydown = (event) => {
|
|
6101
|
+
if (event.key === "Escape") {
|
|
6102
|
+
this.#handleEscapeKey();
|
|
6103
|
+
} else {
|
|
6104
|
+
handleRollingTabIndex(this.#tableToolsButtons, event);
|
|
6105
|
+
}
|
|
6089
6106
|
}
|
|
6090
|
-
}
|
|
6091
6107
|
|
|
6092
|
-
|
|
6108
|
+
#handleEscapeKey() {
|
|
6109
|
+
const cell = this.tableController.currentCell;
|
|
6110
|
+
if (!cell) return
|
|
6093
6111
|
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6112
|
+
this.#editor.update(() => {
|
|
6113
|
+
cell.select();
|
|
6114
|
+
this.#editor.focus();
|
|
6115
|
+
});
|
|
6098
6116
|
|
|
6099
|
-
this.#
|
|
6100
|
-
this.#monitorForCodeBlockSelection();
|
|
6117
|
+
this.#update();
|
|
6101
6118
|
}
|
|
6102
6119
|
|
|
6103
|
-
#
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
this.languagePickerElement.addEventListener("change", () => {
|
|
6107
|
-
this.#updateCodeBlockLanguage(this.languagePickerElement.value);
|
|
6108
|
-
});
|
|
6120
|
+
async #handleCommandButtonHover() {
|
|
6121
|
+
await nextFrame();
|
|
6109
6122
|
|
|
6110
|
-
this
|
|
6111
|
-
this.appendChild(this.languagePickerElement);
|
|
6112
|
-
}
|
|
6123
|
+
this.#clearCellStyles();
|
|
6113
6124
|
|
|
6114
|
-
|
|
6115
|
-
|
|
6125
|
+
const activeElement = this.querySelector("button:hover, button:focus");
|
|
6126
|
+
if (!activeElement) return
|
|
6116
6127
|
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
}
|
|
6128
|
+
const command = {
|
|
6129
|
+
action: activeElement.dataset.action,
|
|
6130
|
+
childType: activeElement.dataset.childType,
|
|
6131
|
+
direction: activeElement.dataset.direction
|
|
6132
|
+
};
|
|
6123
6133
|
|
|
6124
|
-
|
|
6125
|
-
}
|
|
6134
|
+
let cellsToHighlight = null;
|
|
6126
6135
|
|
|
6127
|
-
|
|
6128
|
-
|
|
6136
|
+
switch (command.childType) {
|
|
6137
|
+
case "row":
|
|
6138
|
+
cellsToHighlight = this.tableController.currentRowCells;
|
|
6139
|
+
break
|
|
6140
|
+
case "column":
|
|
6141
|
+
cellsToHighlight = this.tableController.currentColumnCells;
|
|
6142
|
+
break
|
|
6143
|
+
case "table":
|
|
6144
|
+
cellsToHighlight = this.tableController.tableRows;
|
|
6145
|
+
break
|
|
6146
|
+
}
|
|
6129
6147
|
|
|
6130
|
-
if (!
|
|
6131
|
-
if (!languages.php) languages.php = "PHP";
|
|
6132
|
-
if (!languages.go) languages.go = "Go";
|
|
6133
|
-
if (!languages.bash) languages.bash = "Bash";
|
|
6134
|
-
if (!languages.json) languages.json = "JSON";
|
|
6135
|
-
if (!languages.diff) languages.diff = "Diff";
|
|
6148
|
+
if (!cellsToHighlight) return
|
|
6136
6149
|
|
|
6137
|
-
|
|
6138
|
-
|
|
6150
|
+
cellsToHighlight.forEach(cell => {
|
|
6151
|
+
const cellElement = this.#editor.getElementByKey(cell.getKey());
|
|
6152
|
+
if (!cellElement) return
|
|
6139
6153
|
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
return Object.fromEntries([ plainEntry, ...sortedEntries ])
|
|
6154
|
+
cellElement.classList.toggle(theme.tableCellHighlight, true);
|
|
6155
|
+
Object.assign(cellElement.dataset, command);
|
|
6156
|
+
});
|
|
6144
6157
|
}
|
|
6145
6158
|
|
|
6146
|
-
#
|
|
6147
|
-
this.editor.
|
|
6148
|
-
|
|
6159
|
+
#monitorForTableSelection() {
|
|
6160
|
+
this.unregisterUpdateListener = this.#editor.registerUpdateListener(() => {
|
|
6161
|
+
this.tableController.updateSelectedTable();
|
|
6149
6162
|
|
|
6150
|
-
|
|
6151
|
-
|
|
6163
|
+
const tableNode = this.tableController.currentTableNode;
|
|
6164
|
+
if (tableNode) {
|
|
6165
|
+
this.#show();
|
|
6166
|
+
} else {
|
|
6167
|
+
this.#hide();
|
|
6152
6168
|
}
|
|
6153
6169
|
});
|
|
6154
6170
|
}
|
|
6155
6171
|
|
|
6156
|
-
#
|
|
6157
|
-
this.
|
|
6158
|
-
|
|
6159
|
-
|
|
6172
|
+
#executeTableCommand(command) {
|
|
6173
|
+
this.tableController.executeTableCommand(command);
|
|
6174
|
+
this.#update();
|
|
6175
|
+
}
|
|
6160
6176
|
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
this.#hideLanguagePicker();
|
|
6165
|
-
}
|
|
6166
|
-
});
|
|
6167
|
-
});
|
|
6177
|
+
#show() {
|
|
6178
|
+
this.style.display = "flex";
|
|
6179
|
+
this.#update();
|
|
6168
6180
|
}
|
|
6169
6181
|
|
|
6170
|
-
#
|
|
6171
|
-
|
|
6182
|
+
#hide() {
|
|
6183
|
+
this.style.display = "none";
|
|
6184
|
+
this.#clearCellStyles();
|
|
6185
|
+
}
|
|
6172
6186
|
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6187
|
+
#update() {
|
|
6188
|
+
this.#updateButtonsPosition();
|
|
6189
|
+
this.#updateRowColumnCount();
|
|
6190
|
+
this.#closeMoreMenu();
|
|
6191
|
+
this.#handleCommandButtonHover();
|
|
6192
|
+
}
|
|
6176
6193
|
|
|
6177
|
-
|
|
6178
|
-
|
|
6194
|
+
#closeMoreMenu() {
|
|
6195
|
+
this.querySelector("details[open]")?.removeAttribute("open");
|
|
6196
|
+
}
|
|
6179
6197
|
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
return parentNode
|
|
6184
|
-
}
|
|
6198
|
+
#updateButtonsPosition() {
|
|
6199
|
+
const tableNode = this.tableController.currentTableNode;
|
|
6200
|
+
if (!tableNode) return
|
|
6185
6201
|
|
|
6186
|
-
|
|
6187
|
-
|
|
6202
|
+
const tableElement = this.#editor.getElementByKey(tableNode.getKey());
|
|
6203
|
+
if (!tableElement) return
|
|
6188
6204
|
|
|
6189
|
-
|
|
6190
|
-
const
|
|
6205
|
+
const tableRect = tableElement.getBoundingClientRect();
|
|
6206
|
+
const editorRect = this.#editorElement.getBoundingClientRect();
|
|
6191
6207
|
|
|
6192
|
-
|
|
6193
|
-
|
|
6194
|
-
this
|
|
6208
|
+
const relativeTop = tableRect.top - editorRect.top;
|
|
6209
|
+
const relativeCenter = (tableRect.left + tableRect.right) / 2 - editorRect.left;
|
|
6210
|
+
this.style.top = `${relativeTop}px`;
|
|
6211
|
+
this.style.left = `${relativeCenter}px`;
|
|
6195
6212
|
}
|
|
6196
6213
|
|
|
6197
|
-
#
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6214
|
+
#updateRowColumnCount() {
|
|
6215
|
+
const tableNode = this.tableController.currentTableNode;
|
|
6216
|
+
if (!tableNode) return
|
|
6217
|
+
|
|
6218
|
+
const tableElement = $getElementForTableNode(this.#editor, tableNode);
|
|
6219
|
+
if (!tableElement) return
|
|
6220
|
+
|
|
6221
|
+
const rowCount = tableElement.rows;
|
|
6222
|
+
const columnCount = tableElement.columns;
|
|
6223
|
+
|
|
6224
|
+
this.rowCount.textContent = `${rowCount} row${rowCount === 1 ? "" : "s"}`;
|
|
6225
|
+
this.columnCount.textContent = `${columnCount} column${columnCount === 1 ? "" : "s"}`;
|
|
6202
6226
|
}
|
|
6203
6227
|
|
|
6204
|
-
#
|
|
6205
|
-
const
|
|
6206
|
-
if (!
|
|
6228
|
+
#setTableCellFocus() {
|
|
6229
|
+
const cell = this.tableController.currentCell;
|
|
6230
|
+
if (!cell) return
|
|
6207
6231
|
|
|
6208
|
-
const
|
|
6209
|
-
|
|
6210
|
-
const relativeTop = codeRect.top - editorRect.top;
|
|
6211
|
-
const relativeRight = editorRect.right - codeRect.right;
|
|
6232
|
+
const cellElement = this.#editor.getElementByKey(cell.getKey());
|
|
6233
|
+
if (!cellElement) return
|
|
6212
6234
|
|
|
6213
|
-
|
|
6214
|
-
this.style.right = `${relativeRight}px`;
|
|
6235
|
+
cellElement.classList.add(theme.tableCellFocus);
|
|
6215
6236
|
}
|
|
6216
6237
|
|
|
6217
|
-
#
|
|
6218
|
-
this.
|
|
6238
|
+
#clearCellStyles() {
|
|
6239
|
+
this.#editorElement.querySelectorAll(`.${theme.tableCellFocus}`)?.forEach(cell => {
|
|
6240
|
+
cell.classList.remove(theme.tableCellFocus);
|
|
6241
|
+
});
|
|
6242
|
+
|
|
6243
|
+
this.#editorElement.querySelectorAll(`.${theme.tableCellHighlight}`)?.forEach(cell => {
|
|
6244
|
+
cell.classList.remove(theme.tableCellHighlight);
|
|
6245
|
+
cell.removeAttribute("data-action");
|
|
6246
|
+
cell.removeAttribute("data-child-type");
|
|
6247
|
+
cell.removeAttribute("data-direction");
|
|
6248
|
+
});
|
|
6249
|
+
|
|
6250
|
+
this.#setTableCellFocus();
|
|
6219
6251
|
}
|
|
6220
6252
|
|
|
6221
|
-
#
|
|
6222
|
-
|
|
6253
|
+
#icon(command) {
|
|
6254
|
+
const { action, childType } = command;
|
|
6255
|
+
const direction = (action == "insert" ? command.direction : null);
|
|
6256
|
+
const iconId = [ action, childType, direction ].filter(Boolean).join("-");
|
|
6257
|
+
return TableIcons[iconId]
|
|
6223
6258
|
}
|
|
6224
6259
|
}
|
|
6225
6260
|
|
|
6226
|
-
|
|
6261
|
+
function defineElements() {
|
|
6262
|
+
const elements = {
|
|
6263
|
+
"lexxy-toolbar": LexicalToolbarElement,
|
|
6264
|
+
"lexxy-editor": LexicalEditorElement,
|
|
6265
|
+
"lexxy-link-dropdown": LinkDropdown,
|
|
6266
|
+
"lexxy-highlight-dropdown": HighlightDropdown,
|
|
6267
|
+
"lexxy-prompt": LexicalPromptElement,
|
|
6268
|
+
"lexxy-code-language-picker": CodeLanguagePicker,
|
|
6269
|
+
"lexxy-table-tools": TableTools,
|
|
6270
|
+
};
|
|
6271
|
+
|
|
6272
|
+
Object.entries(elements).forEach(([ name, element ]) => {
|
|
6273
|
+
customElements.define(name, element);
|
|
6274
|
+
});
|
|
6275
|
+
}
|
|
6227
6276
|
|
|
6228
6277
|
class LexxyExtension {
|
|
6229
6278
|
#editorElement
|
|
@@ -6256,4 +6305,7 @@ class LexxyExtension {
|
|
|
6256
6305
|
|
|
6257
6306
|
const configure = Lexxy.configure;
|
|
6258
6307
|
|
|
6308
|
+
// Pushing elements definition to after the current call stack to allow global configuration to take place first
|
|
6309
|
+
setTimeout(defineElements, 0);
|
|
6310
|
+
|
|
6259
6311
|
export { ActionTextAttachmentNode, ActionTextAttachmentUploadNode, CustomActionTextAttachmentNode, LexxyExtension as Extension, HorizontalDividerNode, configure };
|