@limetech/lime-elements 39.44.6 → 39.45.1
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/CHANGELOG.md +21 -0
- package/dist/cjs/limel-form.cjs.entry.js +41 -9
- package/dist/cjs/limel-markdown.cjs.entry.js +1 -1
- package/dist/cjs/limel-prosemirror-adapter.cjs.entry.js +778 -395
- package/dist/cjs/limel-text-editor-link-menu.cjs.entry.js +6 -6
- package/dist/cjs/{markdown-parser-ChnRAxpZ.js → markdown-parser-YH5HSSgF.js} +36 -56
- package/dist/collection/components/text-editor/link-menu/editor-link-menu.js +6 -6
- package/dist/collection/components/text-editor/prosemirror-adapter/editor-config.js +12 -1
- package/dist/collection/components/text-editor/prosemirror-adapter/menu/menu-command-utils/active-state-utils.js +36 -0
- package/dist/collection/components/text-editor/prosemirror-adapter/menu/menu-command-utils/list-utils.js +270 -0
- package/dist/collection/components/text-editor/prosemirror-adapter/menu/menu-command-utils/node-utils.js +15 -0
- package/dist/collection/components/text-editor/prosemirror-adapter/menu/menu-commands.js +81 -69
- package/dist/collection/components/text-editor/prosemirror-adapter/plugins/list-key-handler.js +44 -0
- package/dist/collection/components/text-editor/prosemirror-adapter/prosemirror-adapter.js +4 -3
- package/dist/esm/limel-form.entry.js +41 -9
- package/dist/esm/limel-markdown.entry.js +1 -1
- package/dist/esm/limel-prosemirror-adapter.entry.js +779 -396
- package/dist/esm/limel-text-editor-link-menu.entry.js +6 -6
- package/dist/esm/{markdown-parser-BSQKleVw.js → markdown-parser-DrxxGLF5.js} +36 -56
- package/dist/lime-elements/lime-elements.esm.js +1 -1
- package/dist/lime-elements/p-1f29f482.entry.js +1 -0
- package/dist/lime-elements/{p-3ea159fd.entry.js → p-38393fb4.entry.js} +1 -1
- package/dist/lime-elements/{p-1fb4bfa9.entry.js → p-421612f9.entry.js} +2 -2
- package/dist/lime-elements/{p-B0qhUema.js → p-C9xHOCE_.js} +2 -2
- package/dist/lime-elements/p-d97df2cd.entry.js +1 -0
- package/dist/types/components/text-editor/link-menu/editor-link-menu.d.ts +2 -2
- package/dist/types/components/text-editor/prosemirror-adapter/menu/menu-command-utils/active-state-utils.d.ts +6 -0
- package/dist/types/components/text-editor/prosemirror-adapter/menu/menu-command-utils/list-utils.d.ts +68 -0
- package/dist/types/components/text-editor/prosemirror-adapter/menu/menu-command-utils/node-utils.d.ts +10 -0
- package/dist/types/components/text-editor/prosemirror-adapter/menu/menu-commands.d.ts +10 -0
- package/dist/types/components/text-editor/prosemirror-adapter/plugins/list-key-handler.d.ts +14 -0
- package/package.json +4 -4
- package/dist/lime-elements/p-845cfda7.entry.js +0 -1
- package/dist/lime-elements/p-e8df7a2e.entry.js +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { r as registerInstance, c as createEvent, h, H as Host, a as getElement } from './index-BGxJfR2f.js';
|
|
2
|
-
import {
|
|
3
|
-
import { g as getLinkAttributes, m as markdownToHTML, s as sanitizeHTML } from './markdown-parser-
|
|
2
|
+
import { L as LevelMapping, E as EditorMenuTypes, i as isInlineImageTag, M as MouseButtons, e as editorMenuTypesArray } from './types-C_Yu8dOg.js';
|
|
3
|
+
import { g as getLinkAttributes, m as markdownToHTML, s as sanitizeHTML } from './markdown-parser-DrxxGLF5.js';
|
|
4
4
|
import { h as cloneDeep } from './cloneDeep-CL7UD3mR.js';
|
|
5
5
|
import { t as translate } from './translations-Cb9R_zmF.js';
|
|
6
6
|
import { c as decodeHTML, e as decodeHTMLStrict } from './index-CbciMfiU.js';
|
|
@@ -13401,15 +13401,266 @@ Depending on the detected platform, this will hold
|
|
|
13401
13401
|
*/
|
|
13402
13402
|
const baseKeymap = mac$3 ? macBaseKeymap : pcBaseKeymap;
|
|
13403
13403
|
|
|
13404
|
+
const olDOM = ["ol", 0], ulDOM = ["ul", 0], liDOM = ["li", 0];
|
|
13405
|
+
/**
|
|
13406
|
+
An ordered list [node spec](https://prosemirror.net/docs/ref/#model.NodeSpec). Has a single
|
|
13407
|
+
attribute, `order`, which determines the number at which the list
|
|
13408
|
+
starts counting, and defaults to 1. Represented as an `<ol>`
|
|
13409
|
+
element.
|
|
13410
|
+
*/
|
|
13411
|
+
const orderedList = {
|
|
13412
|
+
attrs: { order: { default: 1 } },
|
|
13413
|
+
parseDOM: [{ tag: "ol", getAttrs(dom) {
|
|
13414
|
+
return { order: dom.hasAttribute("start") ? +dom.getAttribute("start") : 1 };
|
|
13415
|
+
} }],
|
|
13416
|
+
toDOM(node) {
|
|
13417
|
+
return node.attrs.order == 1 ? olDOM : ["ol", { start: node.attrs.order }, 0];
|
|
13418
|
+
}
|
|
13419
|
+
};
|
|
13420
|
+
/**
|
|
13421
|
+
A bullet list node spec, represented in the DOM as `<ul>`.
|
|
13422
|
+
*/
|
|
13423
|
+
const bulletList = {
|
|
13424
|
+
parseDOM: [{ tag: "ul" }],
|
|
13425
|
+
toDOM() { return ulDOM; }
|
|
13426
|
+
};
|
|
13427
|
+
/**
|
|
13428
|
+
A list item (`<li>`) spec.
|
|
13429
|
+
*/
|
|
13430
|
+
const listItem = {
|
|
13431
|
+
parseDOM: [{ tag: "li" }],
|
|
13432
|
+
toDOM() { return liDOM; },
|
|
13433
|
+
defining: true
|
|
13434
|
+
};
|
|
13435
|
+
function add(obj, props) {
|
|
13436
|
+
let copy = {};
|
|
13437
|
+
for (let prop in obj)
|
|
13438
|
+
copy[prop] = obj[prop];
|
|
13439
|
+
for (let prop in props)
|
|
13440
|
+
copy[prop] = props[prop];
|
|
13441
|
+
return copy;
|
|
13442
|
+
}
|
|
13443
|
+
/**
|
|
13444
|
+
Convenience function for adding list-related node types to a map
|
|
13445
|
+
specifying the nodes for a schema. Adds
|
|
13446
|
+
[`orderedList`](https://prosemirror.net/docs/ref/#schema-list.orderedList) as `"ordered_list"`,
|
|
13447
|
+
[`bulletList`](https://prosemirror.net/docs/ref/#schema-list.bulletList) as `"bullet_list"`, and
|
|
13448
|
+
[`listItem`](https://prosemirror.net/docs/ref/#schema-list.listItem) as `"list_item"`.
|
|
13449
|
+
|
|
13450
|
+
`itemContent` determines the content expression for the list items.
|
|
13451
|
+
If you want the commands defined in this module to apply to your
|
|
13452
|
+
list structure, it should have a shape like `"paragraph block*"` or
|
|
13453
|
+
`"paragraph (ordered_list | bullet_list)*"`. `listGroup` can be
|
|
13454
|
+
given to assign a group name to the list node types, for example
|
|
13455
|
+
`"block"`.
|
|
13456
|
+
*/
|
|
13457
|
+
function addListNodes(nodes, itemContent, listGroup) {
|
|
13458
|
+
return nodes.append({
|
|
13459
|
+
ordered_list: add(orderedList, { content: "list_item+", group: listGroup }),
|
|
13460
|
+
bullet_list: add(bulletList, { content: "list_item+", group: listGroup }),
|
|
13461
|
+
list_item: add(listItem, { content: itemContent })
|
|
13462
|
+
});
|
|
13463
|
+
}
|
|
13464
|
+
/**
|
|
13465
|
+
Returns a command function that wraps the selection in a list with
|
|
13466
|
+
the given type an attributes. If `dispatch` is null, only return a
|
|
13467
|
+
value to indicate whether this is possible, but don't actually
|
|
13468
|
+
perform the change.
|
|
13469
|
+
*/
|
|
13470
|
+
function wrapInList(listType, attrs = null) {
|
|
13471
|
+
return function (state, dispatch) {
|
|
13472
|
+
let { $from, $to } = state.selection;
|
|
13473
|
+
let range = $from.blockRange($to), doJoin = false, outerRange = range;
|
|
13474
|
+
if (!range)
|
|
13475
|
+
return false;
|
|
13476
|
+
// This is at the top of an existing list item
|
|
13477
|
+
if (range.depth >= 2 && $from.node(range.depth - 1).type.compatibleContent(listType) && range.startIndex == 0) {
|
|
13478
|
+
// Don't do anything if this is the top of the list
|
|
13479
|
+
if ($from.index(range.depth - 1) == 0)
|
|
13480
|
+
return false;
|
|
13481
|
+
let $insert = state.doc.resolve(range.start - 2);
|
|
13482
|
+
outerRange = new NodeRange($insert, $insert, range.depth);
|
|
13483
|
+
if (range.endIndex < range.parent.childCount)
|
|
13484
|
+
range = new NodeRange($from, state.doc.resolve($to.end(range.depth)), range.depth);
|
|
13485
|
+
doJoin = true;
|
|
13486
|
+
}
|
|
13487
|
+
let wrap = findWrapping(outerRange, listType, attrs, range);
|
|
13488
|
+
if (!wrap)
|
|
13489
|
+
return false;
|
|
13490
|
+
if (dispatch)
|
|
13491
|
+
dispatch(doWrapInList(state.tr, range, wrap, doJoin, listType).scrollIntoView());
|
|
13492
|
+
return true;
|
|
13493
|
+
};
|
|
13494
|
+
}
|
|
13495
|
+
function doWrapInList(tr, range, wrappers, joinBefore, listType) {
|
|
13496
|
+
let content = Fragment.empty;
|
|
13497
|
+
for (let i = wrappers.length - 1; i >= 0; i--)
|
|
13498
|
+
content = Fragment.from(wrappers[i].type.create(wrappers[i].attrs, content));
|
|
13499
|
+
tr.step(new ReplaceAroundStep(range.start - (joinBefore ? 2 : 0), range.end, range.start, range.end, new Slice(content, 0, 0), wrappers.length, true));
|
|
13500
|
+
let found = 0;
|
|
13501
|
+
for (let i = 0; i < wrappers.length; i++)
|
|
13502
|
+
if (wrappers[i].type == listType)
|
|
13503
|
+
found = i + 1;
|
|
13504
|
+
let splitDepth = wrappers.length - found;
|
|
13505
|
+
let splitPos = range.start + wrappers.length - (joinBefore ? 2 : 0), parent = range.parent;
|
|
13506
|
+
for (let i = range.startIndex, e = range.endIndex, first = true; i < e; i++, first = false) {
|
|
13507
|
+
if (!first && canSplit(tr.doc, splitPos, splitDepth)) {
|
|
13508
|
+
tr.split(splitPos, splitDepth);
|
|
13509
|
+
splitPos += 2 * splitDepth;
|
|
13510
|
+
}
|
|
13511
|
+
splitPos += parent.child(i).nodeSize;
|
|
13512
|
+
}
|
|
13513
|
+
return tr;
|
|
13514
|
+
}
|
|
13515
|
+
/**
|
|
13516
|
+
Build a command that splits a non-empty textblock at the top level
|
|
13517
|
+
of a list item by also splitting that list item.
|
|
13518
|
+
*/
|
|
13519
|
+
function splitListItem(itemType, itemAttrs) {
|
|
13520
|
+
return function (state, dispatch) {
|
|
13521
|
+
let { $from, $to, node } = state.selection;
|
|
13522
|
+
if ((node && node.isBlock) || $from.depth < 2 || !$from.sameParent($to))
|
|
13523
|
+
return false;
|
|
13524
|
+
let grandParent = $from.node(-1);
|
|
13525
|
+
if (grandParent.type != itemType)
|
|
13526
|
+
return false;
|
|
13527
|
+
if ($from.parent.content.size == 0 && $from.node(-1).childCount == $from.indexAfter(-1)) {
|
|
13528
|
+
// In an empty block. If this is a nested list, the wrapping
|
|
13529
|
+
// list item should be split. Otherwise, bail out and let next
|
|
13530
|
+
// command handle lifting.
|
|
13531
|
+
if ($from.depth == 3 || $from.node(-3).type != itemType ||
|
|
13532
|
+
$from.index(-2) != $from.node(-2).childCount - 1)
|
|
13533
|
+
return false;
|
|
13534
|
+
if (dispatch) {
|
|
13535
|
+
let wrap = Fragment.empty;
|
|
13536
|
+
let depthBefore = $from.index(-1) ? 1 : $from.index(-2) ? 2 : 3;
|
|
13537
|
+
// Build a fragment containing empty versions of the structure
|
|
13538
|
+
// from the outer list item to the parent node of the cursor
|
|
13539
|
+
for (let d = $from.depth - depthBefore; d >= $from.depth - 3; d--)
|
|
13540
|
+
wrap = Fragment.from($from.node(d).copy(wrap));
|
|
13541
|
+
let depthAfter = $from.indexAfter(-1) < $from.node(-2).childCount ? 1
|
|
13542
|
+
: $from.indexAfter(-2) < $from.node(-3).childCount ? 2 : 3;
|
|
13543
|
+
// Add a second list item with an empty default start node
|
|
13544
|
+
wrap = wrap.append(Fragment.from(itemType.createAndFill()));
|
|
13545
|
+
let start = $from.before($from.depth - (depthBefore - 1));
|
|
13546
|
+
let tr = state.tr.replace(start, $from.after(-depthAfter), new Slice(wrap, 4 - depthBefore, 0));
|
|
13547
|
+
let sel = -1;
|
|
13548
|
+
tr.doc.nodesBetween(start, tr.doc.content.size, (node, pos) => {
|
|
13549
|
+
if (sel > -1)
|
|
13550
|
+
return false;
|
|
13551
|
+
if (node.isTextblock && node.content.size == 0)
|
|
13552
|
+
sel = pos + 1;
|
|
13553
|
+
});
|
|
13554
|
+
if (sel > -1)
|
|
13555
|
+
tr.setSelection(Selection.near(tr.doc.resolve(sel)));
|
|
13556
|
+
dispatch(tr.scrollIntoView());
|
|
13557
|
+
}
|
|
13558
|
+
return true;
|
|
13559
|
+
}
|
|
13560
|
+
let nextType = $to.pos == $from.end() ? grandParent.contentMatchAt(0).defaultType : null;
|
|
13561
|
+
let tr = state.tr.delete($from.pos, $to.pos);
|
|
13562
|
+
let types = nextType ? [null, { type: nextType }] : undefined;
|
|
13563
|
+
if (!canSplit(tr.doc, $from.pos, 2, types))
|
|
13564
|
+
return false;
|
|
13565
|
+
if (dispatch)
|
|
13566
|
+
dispatch(tr.split($from.pos, 2, types).scrollIntoView());
|
|
13567
|
+
return true;
|
|
13568
|
+
};
|
|
13569
|
+
}
|
|
13570
|
+
/**
|
|
13571
|
+
Create a command to lift the list item around the selection up into
|
|
13572
|
+
a wrapping list.
|
|
13573
|
+
*/
|
|
13574
|
+
function liftListItem(itemType) {
|
|
13575
|
+
return function (state, dispatch) {
|
|
13576
|
+
let { $from, $to } = state.selection;
|
|
13577
|
+
let range = $from.blockRange($to, node => node.childCount > 0 && node.firstChild.type == itemType);
|
|
13578
|
+
if (!range)
|
|
13579
|
+
return false;
|
|
13580
|
+
if (!dispatch)
|
|
13581
|
+
return true;
|
|
13582
|
+
if ($from.node(range.depth - 1).type == itemType) // Inside a parent list
|
|
13583
|
+
return liftToOuterList(state, dispatch, itemType, range);
|
|
13584
|
+
else // Outer list node
|
|
13585
|
+
return liftOutOfList(state, dispatch, range);
|
|
13586
|
+
};
|
|
13587
|
+
}
|
|
13588
|
+
function liftToOuterList(state, dispatch, itemType, range) {
|
|
13589
|
+
let tr = state.tr, end = range.end, endOfList = range.$to.end(range.depth);
|
|
13590
|
+
if (end < endOfList) {
|
|
13591
|
+
// There are siblings after the lifted items, which must become
|
|
13592
|
+
// children of the last item
|
|
13593
|
+
tr.step(new ReplaceAroundStep(end - 1, endOfList, end, endOfList, new Slice(Fragment.from(itemType.create(null, range.parent.copy())), 1, 0), 1, true));
|
|
13594
|
+
range = new NodeRange(tr.doc.resolve(range.$from.pos), tr.doc.resolve(endOfList), range.depth);
|
|
13595
|
+
}
|
|
13596
|
+
const target = liftTarget(range);
|
|
13597
|
+
if (target == null)
|
|
13598
|
+
return false;
|
|
13599
|
+
tr.lift(range, target);
|
|
13600
|
+
let after = tr.mapping.map(end, -1) - 1;
|
|
13601
|
+
if (canJoin(tr.doc, after))
|
|
13602
|
+
tr.join(after);
|
|
13603
|
+
dispatch(tr.scrollIntoView());
|
|
13604
|
+
return true;
|
|
13605
|
+
}
|
|
13606
|
+
function liftOutOfList(state, dispatch, range) {
|
|
13607
|
+
let tr = state.tr, list = range.parent;
|
|
13608
|
+
// Merge the list items into a single big item
|
|
13609
|
+
for (let pos = range.end, i = range.endIndex - 1, e = range.startIndex; i > e; i--) {
|
|
13610
|
+
pos -= list.child(i).nodeSize;
|
|
13611
|
+
tr.delete(pos - 1, pos + 1);
|
|
13612
|
+
}
|
|
13613
|
+
let $start = tr.doc.resolve(range.start), item = $start.nodeAfter;
|
|
13614
|
+
if (tr.mapping.map(range.end) != range.start + $start.nodeAfter.nodeSize)
|
|
13615
|
+
return false;
|
|
13616
|
+
let atStart = range.startIndex == 0, atEnd = range.endIndex == list.childCount;
|
|
13617
|
+
let parent = $start.node(-1), indexBefore = $start.index(-1);
|
|
13618
|
+
if (!parent.canReplace(indexBefore + (atStart ? 0 : 1), indexBefore + 1, item.content.append(atEnd ? Fragment.empty : Fragment.from(list))))
|
|
13619
|
+
return false;
|
|
13620
|
+
let start = $start.pos, end = start + item.nodeSize;
|
|
13621
|
+
// Strip off the surrounding list. At the sides where we're not at
|
|
13622
|
+
// the end of the list, the existing list is closed. At sides where
|
|
13623
|
+
// this is the end, it is overwritten to its end.
|
|
13624
|
+
tr.step(new ReplaceAroundStep(start - (atStart ? 1 : 0), end + (atEnd ? 1 : 0), start + 1, end - 1, new Slice((atStart ? Fragment.empty : Fragment.from(list.copy(Fragment.empty)))
|
|
13625
|
+
.append(atEnd ? Fragment.empty : Fragment.from(list.copy(Fragment.empty))), atStart ? 0 : 1, atEnd ? 0 : 1), atStart ? 0 : 1));
|
|
13626
|
+
dispatch(tr.scrollIntoView());
|
|
13627
|
+
return true;
|
|
13628
|
+
}
|
|
13629
|
+
/**
|
|
13630
|
+
Create a command to sink the list item around the selection down
|
|
13631
|
+
into an inner list.
|
|
13632
|
+
*/
|
|
13633
|
+
function sinkListItem(itemType) {
|
|
13634
|
+
return function (state, dispatch) {
|
|
13635
|
+
let { $from, $to } = state.selection;
|
|
13636
|
+
let range = $from.blockRange($to, node => node.childCount > 0 && node.firstChild.type == itemType);
|
|
13637
|
+
if (!range)
|
|
13638
|
+
return false;
|
|
13639
|
+
let startIndex = range.startIndex;
|
|
13640
|
+
if (startIndex == 0)
|
|
13641
|
+
return false;
|
|
13642
|
+
let parent = range.parent, nodeBefore = parent.child(startIndex - 1);
|
|
13643
|
+
if (nodeBefore.type != itemType)
|
|
13644
|
+
return false;
|
|
13645
|
+
if (dispatch) {
|
|
13646
|
+
let nestedBefore = nodeBefore.lastChild && nodeBefore.lastChild.type == parent.type;
|
|
13647
|
+
let inner = Fragment.from(nestedBefore ? itemType.create() : null);
|
|
13648
|
+
let slice = new Slice(Fragment.from(itemType.create(null, Fragment.from(parent.type.create(null, inner)))), nestedBefore ? 3 : 1, 0);
|
|
13649
|
+
let before = range.start, after = range.end;
|
|
13650
|
+
dispatch(state.tr.step(new ReplaceAroundStep(before - (nestedBefore ? 3 : 1), after, before, after, slice, 1, true))
|
|
13651
|
+
.scrollIntoView());
|
|
13652
|
+
}
|
|
13653
|
+
return true;
|
|
13654
|
+
};
|
|
13655
|
+
}
|
|
13656
|
+
|
|
13404
13657
|
const setActiveMethodForMark = (command, markType) => {
|
|
13405
13658
|
command.active = (state) => {
|
|
13406
13659
|
const { from, $from, to, empty } = state.selection;
|
|
13407
13660
|
if (empty) {
|
|
13408
13661
|
return !!markType.isInSet(state.storedMarks || $from.marks());
|
|
13409
13662
|
}
|
|
13410
|
-
|
|
13411
|
-
return state.doc.rangeHasMark(from, to, markType);
|
|
13412
|
-
}
|
|
13663
|
+
return state.doc.rangeHasMark(from, to, markType);
|
|
13413
13664
|
};
|
|
13414
13665
|
};
|
|
13415
13666
|
const setActiveMethodForNode = (command, nodeType, level) => {
|
|
@@ -13422,24 +13673,292 @@ const setActiveMethodForNode = (command, nodeType, level) => {
|
|
|
13422
13673
|
}
|
|
13423
13674
|
return true;
|
|
13424
13675
|
}
|
|
13425
|
-
return false;
|
|
13426
|
-
};
|
|
13676
|
+
return false;
|
|
13677
|
+
};
|
|
13678
|
+
};
|
|
13679
|
+
const setActiveMethodForWrap = (command, nodeType) => {
|
|
13680
|
+
command.active = (state) => {
|
|
13681
|
+
const { from, to } = state.selection;
|
|
13682
|
+
let found = false;
|
|
13683
|
+
state.doc.nodesBetween(from, to, (node) => {
|
|
13684
|
+
if (node.type === nodeType) {
|
|
13685
|
+
found = true;
|
|
13686
|
+
}
|
|
13687
|
+
return !found;
|
|
13688
|
+
});
|
|
13689
|
+
return found;
|
|
13690
|
+
};
|
|
13691
|
+
};
|
|
13692
|
+
|
|
13693
|
+
var __rest = (undefined && undefined.__rest) || function (s, e) {
|
|
13694
|
+
var t = {};
|
|
13695
|
+
for (var p in s)
|
|
13696
|
+
if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
13697
|
+
t[p] = s[p];
|
|
13698
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
13699
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
13700
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
13701
|
+
t[p[i]] = s[p[i]];
|
|
13702
|
+
}
|
|
13703
|
+
return t;
|
|
13704
|
+
};
|
|
13705
|
+
/**
|
|
13706
|
+
* Whether the selection touches top-level blocks that cannot become list
|
|
13707
|
+
* items (list_item requires a leading paragraph, so headings, blockquotes,
|
|
13708
|
+
* code blocks and tables are not listable).
|
|
13709
|
+
*
|
|
13710
|
+
* @param state - the current editor state
|
|
13711
|
+
* @returns true when a non-listable block intersects the selection
|
|
13712
|
+
*/
|
|
13713
|
+
const selectionHasNonListableBlock = (state) => {
|
|
13714
|
+
const { from, to } = state.selection;
|
|
13715
|
+
const { schema } = state;
|
|
13716
|
+
let found = false;
|
|
13717
|
+
state.doc.nodesBetween(from, to, (node, _pos, parent) => {
|
|
13718
|
+
if (found) {
|
|
13719
|
+
return false;
|
|
13720
|
+
}
|
|
13721
|
+
const isTopLevelBlock = (parent === null || parent === void 0 ? void 0 : parent.type) === schema.topNodeType;
|
|
13722
|
+
const listable = node.type === schema.nodes.paragraph ||
|
|
13723
|
+
node.type === schema.nodes.list_item ||
|
|
13724
|
+
isListNode(node, schema);
|
|
13725
|
+
if (isTopLevelBlock && !listable) {
|
|
13726
|
+
found = true;
|
|
13727
|
+
return false;
|
|
13728
|
+
}
|
|
13729
|
+
return !isTopLevelBlock || isListNode(node, schema);
|
|
13730
|
+
});
|
|
13731
|
+
return found;
|
|
13732
|
+
};
|
|
13733
|
+
/**
|
|
13734
|
+
* Classifies the selection for the list command ladder.
|
|
13735
|
+
*
|
|
13736
|
+
* @param state - the current editor state
|
|
13737
|
+
* @param listType - the list type the command targets
|
|
13738
|
+
* @returns the ladder branch to take, plus the innermost list depth when
|
|
13739
|
+
* the selection sits inside a list
|
|
13740
|
+
*/
|
|
13741
|
+
const resolveListContext = (state, listType) => {
|
|
13742
|
+
const { $from, $to } = state.selection;
|
|
13743
|
+
const fromDepth = innermostListDepth(state);
|
|
13744
|
+
const sameTopLevelBlock = state.selection.empty ||
|
|
13745
|
+
$from.sameParent($to) ||
|
|
13746
|
+
$from.node(1) === $to.node(1);
|
|
13747
|
+
if (fromDepth !== null && sameTopLevelBlock) {
|
|
13748
|
+
return listContextAt($from, fromDepth, listType);
|
|
13749
|
+
}
|
|
13750
|
+
if (fromDepth === null && sameTopLevelBlock) {
|
|
13751
|
+
return { kind: 'no-list', listDepth: null, singleBlock: true };
|
|
13752
|
+
}
|
|
13753
|
+
// The range spans multiple top-level blocks: uniform paragraphs mean a
|
|
13754
|
+
// plain wrap; lists that are all of the target type mean a toggle;
|
|
13755
|
+
// any other list in the mix means unification.
|
|
13756
|
+
const listTypes = new Set();
|
|
13757
|
+
let sawParagraph = false;
|
|
13758
|
+
const startIndex = $from.index(0);
|
|
13759
|
+
const endIndex = Math.min($to.index(0), state.doc.childCount - 1);
|
|
13760
|
+
for (let i = startIndex; i <= endIndex; i++) {
|
|
13761
|
+
const child = state.doc.child(i);
|
|
13762
|
+
if (isListNode(child, state.schema)) {
|
|
13763
|
+
listTypes.add(child.type);
|
|
13764
|
+
}
|
|
13765
|
+
else if (child.type === state.schema.nodes.paragraph) {
|
|
13766
|
+
sawParagraph = true;
|
|
13767
|
+
}
|
|
13768
|
+
}
|
|
13769
|
+
const uniformTargetLists = !sawParagraph && listTypes.size === 1 && listTypes.has(listType);
|
|
13770
|
+
if (uniformTargetLists && fromDepth !== null) {
|
|
13771
|
+
return listContextAt($from, fromDepth, listType);
|
|
13772
|
+
}
|
|
13773
|
+
if (listTypes.size > 0) {
|
|
13774
|
+
return { kind: 'mixed', listDepth: null };
|
|
13775
|
+
}
|
|
13776
|
+
return { kind: 'no-list', listDepth: null, singleBlock: false };
|
|
13777
|
+
};
|
|
13778
|
+
/**
|
|
13779
|
+
* Converts the innermost list around the selection to the target list type.
|
|
13780
|
+
* bullet_list and ordered_list share the list_item+ content model, so the
|
|
13781
|
+
* node can change type in place.
|
|
13782
|
+
*
|
|
13783
|
+
* @param state - the current editor state
|
|
13784
|
+
* @param listDepth - depth of the list node, from resolveListContext
|
|
13785
|
+
* @param targetType - the list type to convert to
|
|
13786
|
+
* @param dispatch - the dispatch function; omit for a dry-run capability check
|
|
13787
|
+
* @returns true when the conversion applies
|
|
13788
|
+
*/
|
|
13789
|
+
const convertInnermostList = (state, listDepth, targetType, dispatch) => {
|
|
13790
|
+
const { $from } = state.selection;
|
|
13791
|
+
const pos = $from.before(listDepth);
|
|
13792
|
+
const node = state.doc.nodeAt(pos);
|
|
13793
|
+
if (!node) {
|
|
13794
|
+
return false;
|
|
13795
|
+
}
|
|
13796
|
+
if (dispatch) {
|
|
13797
|
+
dispatch(state.tr
|
|
13798
|
+
.setNodeMarkup(pos, targetType, listAttrsFor(targetType, node.attrs, state.schema))
|
|
13799
|
+
.scrollIntoView());
|
|
13800
|
+
}
|
|
13801
|
+
return true;
|
|
13802
|
+
};
|
|
13803
|
+
/**
|
|
13804
|
+
* Turns a mixed top-level selection (paragraphs and lists) into a single
|
|
13805
|
+
* list of the target type: paragraph runs are wrapped, lists are converted
|
|
13806
|
+
* in place, and adjacent same-type lists are joined. Nesting inside
|
|
13807
|
+
* converted lists is preserved.
|
|
13808
|
+
*
|
|
13809
|
+
* @param state - the current editor state
|
|
13810
|
+
* @param targetType - the list type to unify into
|
|
13811
|
+
* @param dispatch - the dispatch function; omit for a dry-run capability check
|
|
13812
|
+
* @returns true when the unification applies
|
|
13813
|
+
*/
|
|
13814
|
+
const unifyToList = (state, targetType, dispatch) => {
|
|
13815
|
+
const { $from, $to } = state.selection;
|
|
13816
|
+
if ($from.depth === 0 || $to.depth === 0) {
|
|
13817
|
+
return false;
|
|
13818
|
+
}
|
|
13819
|
+
if (dispatch === undefined) {
|
|
13820
|
+
return true;
|
|
13821
|
+
}
|
|
13822
|
+
const tr = state.tr;
|
|
13823
|
+
convertListsAndWrapRuns(tr, state, targetType, $from.index(0), $to.index(0));
|
|
13824
|
+
joinAdjacentLists(tr, targetType, tr.mapping.map($from.before(1)), mapKeepBefore(tr, $to.after(1)));
|
|
13825
|
+
dispatch(tr.scrollIntoView());
|
|
13826
|
+
return true;
|
|
13827
|
+
};
|
|
13828
|
+
/**
|
|
13829
|
+
* Wraps a dispatch so that after a list is wrapped around the selection,
|
|
13830
|
+
* adjacent top-level lists of the same type are joined with it.
|
|
13831
|
+
*
|
|
13832
|
+
* @param state - the editor state the wrap command runs on
|
|
13833
|
+
* @param targetType - the list type being wrapped
|
|
13834
|
+
* @param dispatch - the dispatch function; omitted for a dry-run capability check
|
|
13835
|
+
* @returns the wrapped dispatch, or undefined when no dispatch was given
|
|
13836
|
+
*/
|
|
13837
|
+
const joinAdjacentListsOnDispatch = (state, targetType, dispatch) => {
|
|
13838
|
+
if (!dispatch) {
|
|
13839
|
+
return undefined;
|
|
13840
|
+
}
|
|
13841
|
+
const { $from, $to } = state.selection;
|
|
13842
|
+
return (tr) => {
|
|
13843
|
+
// The new list's outer boundaries: the start maps before the
|
|
13844
|
+
// inserted wrapping, the end maps after it.
|
|
13845
|
+
const start = mapKeepBefore(tr, $from.before(1));
|
|
13846
|
+
const end = tr.mapping.map($to.after(1));
|
|
13847
|
+
// Back to front, so the earlier boundary stays valid after a join.
|
|
13848
|
+
joinSameTypeBoundary(tr, targetType, end);
|
|
13849
|
+
joinSameTypeBoundary(tr, targetType, start);
|
|
13850
|
+
dispatch(tr);
|
|
13851
|
+
};
|
|
13852
|
+
};
|
|
13853
|
+
const joinSameTypeBoundary = (tr, targetType, boundary) => {
|
|
13854
|
+
var _a, _b;
|
|
13855
|
+
if (boundary <= 0 || boundary >= tr.doc.content.size) {
|
|
13856
|
+
return;
|
|
13857
|
+
}
|
|
13858
|
+
const $boundary = tr.doc.resolve(boundary);
|
|
13859
|
+
if (((_a = $boundary.nodeBefore) === null || _a === void 0 ? void 0 : _a.type) === targetType &&
|
|
13860
|
+
((_b = $boundary.nodeAfter) === null || _b === void 0 ? void 0 : _b.type) === targetType) {
|
|
13861
|
+
tr.join(boundary);
|
|
13862
|
+
}
|
|
13863
|
+
};
|
|
13864
|
+
const isListNode = (node, schema) => node.type === schema.nodes.bullet_list ||
|
|
13865
|
+
node.type === schema.nodes.ordered_list;
|
|
13866
|
+
const innermostListDepth = (state) => {
|
|
13867
|
+
const { $from } = state.selection;
|
|
13868
|
+
for (let depth = $from.depth; depth > 0; depth--) {
|
|
13869
|
+
if (isListNode($from.node(depth), state.schema)) {
|
|
13870
|
+
return depth;
|
|
13871
|
+
}
|
|
13872
|
+
}
|
|
13873
|
+
return null;
|
|
13874
|
+
};
|
|
13875
|
+
const listContextAt = ($from, listDepth, listType) => ({
|
|
13876
|
+
kind: $from.node(listDepth).type === listType ? 'same-type' : 'other-type',
|
|
13877
|
+
listDepth: listDepth,
|
|
13878
|
+
});
|
|
13879
|
+
const listAttrsFor = (targetType, attrs, schema) => {
|
|
13880
|
+
var _a;
|
|
13881
|
+
if (targetType === schema.nodes.ordered_list) {
|
|
13882
|
+
return Object.assign(Object.assign({}, attrs), { order: (_a = attrs.order) !== null && _a !== void 0 ? _a : 1 });
|
|
13883
|
+
}
|
|
13884
|
+
const withoutOrder = __rest(attrs, ["order"]);
|
|
13885
|
+
return withoutOrder;
|
|
13886
|
+
};
|
|
13887
|
+
const mapKeepBefore = (tr, position) =>
|
|
13888
|
+
// ProseMirror's Mapping.map takes (pos, assoc); the unicorn rule
|
|
13889
|
+
// misreads it as Array#map with a `this` argument.
|
|
13890
|
+
// eslint-disable-next-line unicorn/no-array-method-this-argument
|
|
13891
|
+
tr.mapping.map(position, -1);
|
|
13892
|
+
const wrapRunInList = (tr, targetType, runStart, runEnd) => {
|
|
13893
|
+
const $start = tr.doc.resolve(tr.mapping.map(runStart) + 1);
|
|
13894
|
+
const $end = tr.doc.resolve(mapKeepBefore(tr, runEnd) - 1);
|
|
13895
|
+
const range = new NodeRange($start, $end, 0);
|
|
13896
|
+
const wrapping = findWrapping(range, targetType);
|
|
13897
|
+
if (!wrapping) {
|
|
13898
|
+
return;
|
|
13899
|
+
}
|
|
13900
|
+
tr.wrap(range, wrapping);
|
|
13901
|
+
// tr.wrap puts the whole run inside a single list_item; splitting at
|
|
13902
|
+
// each block boundary gives one item per block, the same shape
|
|
13903
|
+
// wrapInList produces.
|
|
13904
|
+
const listIndex = wrapping.findIndex((entry) => entry.type === targetType);
|
|
13905
|
+
const splitDepth = wrapping.length - (listIndex + 1);
|
|
13906
|
+
let splitPos = range.start + wrapping.length;
|
|
13907
|
+
for (let i = range.startIndex; i < range.endIndex; i++) {
|
|
13908
|
+
if (i > range.startIndex && canSplit(tr.doc, splitPos, splitDepth)) {
|
|
13909
|
+
tr.split(splitPos, splitDepth);
|
|
13910
|
+
splitPos += 2 * splitDepth;
|
|
13911
|
+
}
|
|
13912
|
+
splitPos += range.parent.child(i).nodeSize;
|
|
13913
|
+
}
|
|
13914
|
+
};
|
|
13915
|
+
const convertListsAndWrapRuns = (tr, state, targetType, startIndex, endIndex) => {
|
|
13916
|
+
// Positions computed against the pre-transform doc are mapped through
|
|
13917
|
+
// tr.mapping when applied.
|
|
13918
|
+
const doc = state.doc;
|
|
13919
|
+
let runStart = null;
|
|
13920
|
+
let runEnd = null;
|
|
13921
|
+
let pos = 0;
|
|
13922
|
+
for (let i = 0; i < doc.childCount; i++) {
|
|
13923
|
+
const child = doc.child(i);
|
|
13924
|
+
const inRange = i >= startIndex && i <= endIndex;
|
|
13925
|
+
const isList = isListNode(child, state.schema);
|
|
13926
|
+
if (inRange && child.type === state.schema.nodes.paragraph) {
|
|
13927
|
+
runStart = runStart !== null && runStart !== void 0 ? runStart : pos;
|
|
13928
|
+
runEnd = pos + child.nodeSize;
|
|
13929
|
+
}
|
|
13930
|
+
else if (runStart !== null && runEnd !== null) {
|
|
13931
|
+
wrapRunInList(tr, targetType, runStart, runEnd);
|
|
13932
|
+
runStart = null;
|
|
13933
|
+
runEnd = null;
|
|
13934
|
+
}
|
|
13935
|
+
if (inRange && isList && child.type !== targetType) {
|
|
13936
|
+
tr.setNodeMarkup(tr.mapping.map(pos), targetType, listAttrsFor(targetType, child.attrs, state.schema));
|
|
13937
|
+
}
|
|
13938
|
+
pos += child.nodeSize;
|
|
13939
|
+
}
|
|
13940
|
+
if (runStart !== null && runEnd !== null) {
|
|
13941
|
+
wrapRunInList(tr, targetType, runStart, runEnd);
|
|
13942
|
+
}
|
|
13427
13943
|
};
|
|
13428
|
-
const
|
|
13429
|
-
|
|
13430
|
-
|
|
13431
|
-
|
|
13432
|
-
|
|
13433
|
-
|
|
13434
|
-
|
|
13435
|
-
|
|
13436
|
-
|
|
13437
|
-
|
|
13438
|
-
|
|
13944
|
+
const joinAdjacentLists = (tr, targetType, mappedFrom, mappedTo) => {
|
|
13945
|
+
// Scan the transformed doc's top-level boundaries back to front, so
|
|
13946
|
+
// earlier join positions stay valid as joins shrink the doc.
|
|
13947
|
+
const boundaries = [];
|
|
13948
|
+
let boundary = 0;
|
|
13949
|
+
for (let i = 0; i < tr.doc.childCount; i++) {
|
|
13950
|
+
boundary += tr.doc.child(i).nodeSize;
|
|
13951
|
+
boundaries.push(boundary);
|
|
13952
|
+
}
|
|
13953
|
+
for (let i = boundaries.length - 1; i >= 0; i--) {
|
|
13954
|
+
const joinPos = boundaries[i];
|
|
13955
|
+
if (joinPos <= mappedFrom || joinPos > mappedTo + 1) {
|
|
13956
|
+
continue;
|
|
13439
13957
|
}
|
|
13440
|
-
|
|
13441
|
-
}
|
|
13958
|
+
joinSameTypeBoundary(tr, targetType, joinPos);
|
|
13959
|
+
}
|
|
13442
13960
|
};
|
|
13961
|
+
|
|
13443
13962
|
const createInsertLinkCommand = (schema, _, link) => {
|
|
13444
13963
|
const command = (state, dispatch) => {
|
|
13445
13964
|
const { from, to } = state.selection;
|
|
@@ -13499,11 +14018,13 @@ const toggleNodeType = (schema, type, attrs = {}, shouldWrap = false) => {
|
|
|
13499
14018
|
const paragraphType = schema.nodes.paragraph;
|
|
13500
14019
|
return (state, dispatch) => {
|
|
13501
14020
|
const { $from, $to } = state.selection;
|
|
13502
|
-
const hasActiveWrap = $from.node($from.depth - 1).type === nodeType;
|
|
13503
14021
|
if (state.selection instanceof TextSelection &&
|
|
13504
14022
|
// Ensure selection is within the same parent block
|
|
13505
14023
|
// We don't want toggling block types across multiple blocks
|
|
13506
14024
|
$from.sameParent($from.doc.resolve($to.pos))) {
|
|
14025
|
+
// Resolved only under the TextSelection guard: an AllSelection
|
|
14026
|
+
// resolves at depth 0, where there is no parent node to read.
|
|
14027
|
+
const hasActiveWrap = $from.node($from.depth - 1).type === nodeType;
|
|
13507
14028
|
if ($from.parent.type === nodeType) {
|
|
13508
14029
|
if (dispatch) {
|
|
13509
14030
|
dispatch(state.tr.setBlockType($from.pos, $to.pos, paragraphType));
|
|
@@ -13560,40 +14081,85 @@ const createWrapInCommand = (schema, nodeType) => {
|
|
|
13560
14081
|
setActiveMethodForWrap(command, type);
|
|
13561
14082
|
return command;
|
|
13562
14083
|
};
|
|
13563
|
-
|
|
13564
|
-
|
|
13565
|
-
|
|
13566
|
-
|
|
13567
|
-
|
|
14084
|
+
// Select-all produces an AllSelection, whose endpoints resolve at the
|
|
14085
|
+
// document level where list commands cannot operate. Re-anchoring it as
|
|
14086
|
+
// a TextSelection over the same content lets the list ladder (and the
|
|
14087
|
+
// delegated prosemirror-schema-list commands) treat select-all like any
|
|
14088
|
+
// other full-content selection.
|
|
14089
|
+
const withTextSelection = (state) => {
|
|
14090
|
+
if (!(state.selection instanceof AllSelection)) {
|
|
14091
|
+
return state;
|
|
14092
|
+
}
|
|
14093
|
+
const selection = TextSelection.between(state.doc.resolve(0), state.doc.resolve(state.doc.content.size));
|
|
14094
|
+
// A plugin-free scratch state on the shared doc: only feed it
|
|
14095
|
+
// commands driven by doc and selection.
|
|
14096
|
+
return EditorState.create({
|
|
14097
|
+
doc: state.doc,
|
|
14098
|
+
selection: selection,
|
|
14099
|
+
storedMarks: state.storedMarks,
|
|
14100
|
+
});
|
|
14101
|
+
};
|
|
14102
|
+
/**
|
|
14103
|
+
* Creates a command for toggling list types.
|
|
14104
|
+
*
|
|
14105
|
+
* @param schema - The ProseMirror schema.
|
|
14106
|
+
* @param listTypeName - The name of the list type to toggle.
|
|
14107
|
+
* @returns A command for toggling list types.
|
|
14108
|
+
*/
|
|
14109
|
+
const createListCommand = (schema, listTypeName) => {
|
|
14110
|
+
const type = schema.nodes[listTypeName];
|
|
14111
|
+
if (!type) {
|
|
14112
|
+
throw new Error(`List type "${listTypeName}" not found in schema`);
|
|
14113
|
+
}
|
|
14114
|
+
const itemType = schema.nodes.list_item;
|
|
14115
|
+
if (!itemType) {
|
|
14116
|
+
throw new Error('Node type "list_item" not found in schema');
|
|
14117
|
+
}
|
|
14118
|
+
// The keymap invokes the command directly, so the command body applies
|
|
14119
|
+
// the same applicability rules as `allowed` to keep keyboard and
|
|
14120
|
+
// toolbar behavior identical.
|
|
14121
|
+
const command = (state, dispatch) => {
|
|
14122
|
+
state = withTextSelection(state);
|
|
14123
|
+
if (!(state.selection instanceof TextSelection)) {
|
|
13568
14124
|
return false;
|
|
13569
14125
|
}
|
|
13570
|
-
const
|
|
13571
|
-
if (
|
|
13572
|
-
|
|
13573
|
-
if (dispatch) {
|
|
13574
|
-
dispatch(state.tr.wrap(range, wrapping).scrollIntoView());
|
|
13575
|
-
}
|
|
13576
|
-
return true;
|
|
14126
|
+
const context = resolveListContext(state, type);
|
|
14127
|
+
if (context.kind === 'same-type') {
|
|
14128
|
+
return liftListItem(itemType)(state, dispatch);
|
|
13577
14129
|
}
|
|
13578
|
-
|
|
13579
|
-
|
|
13580
|
-
|
|
13581
|
-
|
|
13582
|
-
|
|
13583
|
-
|
|
13584
|
-
|
|
13585
|
-
|
|
14130
|
+
if (context.kind === 'other-type') {
|
|
14131
|
+
return convertInnermostList(state, context.listDepth, type, dispatch);
|
|
14132
|
+
}
|
|
14133
|
+
if (context.kind === 'no-list') {
|
|
14134
|
+
// For a single block the schema decides, through the wrap
|
|
14135
|
+
// command itself; multi-block selections keep the stricter
|
|
14136
|
+
// gate so unify cannot half-apply.
|
|
14137
|
+
if (!context.singleBlock && selectionHasNonListableBlock(state)) {
|
|
14138
|
+
return false;
|
|
13586
14139
|
}
|
|
14140
|
+
return wrapInList(type)(state, joinAdjacentListsOnDispatch(state, type, dispatch));
|
|
14141
|
+
}
|
|
14142
|
+
if (selectionHasNonListableBlock(state)) {
|
|
13587
14143
|
return false;
|
|
13588
14144
|
}
|
|
14145
|
+
return unifyToList(state, type, dispatch);
|
|
14146
|
+
};
|
|
14147
|
+
command.allowed = (state) => {
|
|
14148
|
+
state = withTextSelection(state);
|
|
14149
|
+
if (!(state.selection instanceof TextSelection)) {
|
|
14150
|
+
return false;
|
|
14151
|
+
}
|
|
14152
|
+
const context = resolveListContext(state, type);
|
|
14153
|
+
// Inside a list, toggling or converting is always applicable, even
|
|
14154
|
+
// when a non-listable ancestor wraps the list.
|
|
14155
|
+
if (context.kind === 'same-type' || context.kind === 'other-type') {
|
|
14156
|
+
return true;
|
|
14157
|
+
}
|
|
14158
|
+
if (context.kind === 'no-list' && context.singleBlock) {
|
|
14159
|
+
return wrapInList(type)(state);
|
|
14160
|
+
}
|
|
14161
|
+
return !selectionHasNonListableBlock(state);
|
|
13589
14162
|
};
|
|
13590
|
-
};
|
|
13591
|
-
const createListCommand = (schema, listType) => {
|
|
13592
|
-
const type = schema.nodes[listType];
|
|
13593
|
-
if (!type) {
|
|
13594
|
-
throw new Error(`List type "${listType}" not found in schema`);
|
|
13595
|
-
}
|
|
13596
|
-
const command = toggleList(type);
|
|
13597
14163
|
setActiveMethodForWrap(command, type);
|
|
13598
14164
|
return command;
|
|
13599
14165
|
};
|
|
@@ -13630,6 +14196,8 @@ class MenuCommandFactory {
|
|
|
13630
14196
|
'Mod-Shift-1': this.getCommand(EditorMenuTypes.HeaderLevel1),
|
|
13631
14197
|
'Mod-Shift-2': this.getCommand(EditorMenuTypes.HeaderLevel2),
|
|
13632
14198
|
'Mod-Shift-3': this.getCommand(EditorMenuTypes.HeaderLevel3),
|
|
14199
|
+
'Mod-Shift-7': this.getCommand(EditorMenuTypes.OrderedList),
|
|
14200
|
+
'Mod-Shift-8': this.getCommand(EditorMenuTypes.BulletList),
|
|
13633
14201
|
'Mod-Shift-X': this.getCommand(EditorMenuTypes.Strikethrough),
|
|
13634
14202
|
'Mod-`': this.getCommand(EditorMenuTypes.Code),
|
|
13635
14203
|
'Mod-Shift-C': this.getCommand(EditorMenuTypes.CodeBlock),
|
|
@@ -23081,356 +23649,103 @@ const nodes = {
|
|
|
23081
23649
|
`alt`, and `href` attributes. The latter two default to the empty
|
|
23082
23650
|
string.
|
|
23083
23651
|
*/
|
|
23084
|
-
image: {
|
|
23085
|
-
inline: true,
|
|
23086
|
-
attrs: {
|
|
23087
|
-
src: { validate: "string" },
|
|
23088
|
-
alt: { default: null, validate: "string|null" },
|
|
23089
|
-
title: { default: null, validate: "string|null" }
|
|
23090
|
-
},
|
|
23091
|
-
group: "inline",
|
|
23092
|
-
draggable: true,
|
|
23093
|
-
parseDOM: [{ tag: "img[src]", getAttrs(dom) {
|
|
23094
|
-
return {
|
|
23095
|
-
src: dom.getAttribute("src"),
|
|
23096
|
-
title: dom.getAttribute("title"),
|
|
23097
|
-
alt: dom.getAttribute("alt")
|
|
23098
|
-
};
|
|
23099
|
-
} }],
|
|
23100
|
-
toDOM(node) { let { src, alt, title } = node.attrs; return ["img", { src, alt, title }]; }
|
|
23101
|
-
},
|
|
23102
|
-
/**
|
|
23103
|
-
A hard line break, represented in the DOM as `<br>`.
|
|
23104
|
-
*/
|
|
23105
|
-
hard_break: {
|
|
23106
|
-
inline: true,
|
|
23107
|
-
group: "inline",
|
|
23108
|
-
selectable: false,
|
|
23109
|
-
parseDOM: [{ tag: "br" }],
|
|
23110
|
-
toDOM() { return brDOM; }
|
|
23111
|
-
}
|
|
23112
|
-
};
|
|
23113
|
-
const emDOM = ["em", 0], strongDOM = ["strong", 0], codeDOM = ["code", 0];
|
|
23114
|
-
/**
|
|
23115
|
-
[Specs](https://prosemirror.net/docs/ref/#model.MarkSpec) for the marks in the schema.
|
|
23116
|
-
*/
|
|
23117
|
-
const marks = {
|
|
23118
|
-
/**
|
|
23119
|
-
A link. Has `href` and `title` attributes. `title`
|
|
23120
|
-
defaults to the empty string. Rendered and parsed as an `<a>`
|
|
23121
|
-
element.
|
|
23122
|
-
*/
|
|
23123
|
-
link: {
|
|
23124
|
-
attrs: {
|
|
23125
|
-
href: { validate: "string" },
|
|
23126
|
-
title: { default: null, validate: "string|null" }
|
|
23127
|
-
},
|
|
23128
|
-
inclusive: false,
|
|
23129
|
-
parseDOM: [{ tag: "a[href]", getAttrs(dom) {
|
|
23130
|
-
return { href: dom.getAttribute("href"), title: dom.getAttribute("title") };
|
|
23131
|
-
} }],
|
|
23132
|
-
toDOM(node) { let { href, title } = node.attrs; return ["a", { href, title }, 0]; }
|
|
23133
|
-
},
|
|
23134
|
-
/**
|
|
23135
|
-
An emphasis mark. Rendered as an `<em>` element. Has parse rules
|
|
23136
|
-
that also match `<i>` and `font-style: italic`.
|
|
23137
|
-
*/
|
|
23138
|
-
em: {
|
|
23139
|
-
parseDOM: [
|
|
23140
|
-
{ tag: "i" }, { tag: "em" },
|
|
23141
|
-
{ style: "font-style=italic" },
|
|
23142
|
-
{ style: "font-style=normal", clearMark: m => m.type.name == "em" }
|
|
23143
|
-
],
|
|
23144
|
-
toDOM() { return emDOM; }
|
|
23145
|
-
},
|
|
23146
|
-
/**
|
|
23147
|
-
A strong mark. Rendered as `<strong>`, parse rules also match
|
|
23148
|
-
`<b>` and `font-weight: bold`.
|
|
23149
|
-
*/
|
|
23150
|
-
strong: {
|
|
23151
|
-
parseDOM: [
|
|
23152
|
-
{ tag: "strong" },
|
|
23153
|
-
// This works around a Google Docs misbehavior where
|
|
23154
|
-
// pasted content will be inexplicably wrapped in `<b>`
|
|
23155
|
-
// tags with a font-weight normal.
|
|
23156
|
-
{ tag: "b", getAttrs: (node) => node.style.fontWeight != "normal" && null },
|
|
23157
|
-
{ style: "font-weight=400", clearMark: m => m.type.name == "strong" },
|
|
23158
|
-
{ style: "font-weight", getAttrs: (value) => /^(bold(er)?|[5-9]\d{2,})$/.test(value) && null },
|
|
23159
|
-
],
|
|
23160
|
-
toDOM() { return strongDOM; }
|
|
23161
|
-
},
|
|
23162
|
-
/**
|
|
23163
|
-
Code font mark. Represented as a `<code>` element.
|
|
23164
|
-
*/
|
|
23165
|
-
code: {
|
|
23166
|
-
code: true,
|
|
23167
|
-
parseDOM: [{ tag: "code" }],
|
|
23168
|
-
toDOM() { return codeDOM; }
|
|
23169
|
-
}
|
|
23170
|
-
};
|
|
23171
|
-
/**
|
|
23172
|
-
This schema roughly corresponds to the document schema used by
|
|
23173
|
-
[CommonMark](http://commonmark.org/), minus the list elements,
|
|
23174
|
-
which are defined in the [`prosemirror-schema-list`](https://prosemirror.net/docs/ref/#schema-list)
|
|
23175
|
-
module.
|
|
23176
|
-
|
|
23177
|
-
To reuse elements from this schema, extend or read from its
|
|
23178
|
-
`spec.nodes` and `spec.marks` [properties](https://prosemirror.net/docs/ref/#model.Schema.spec).
|
|
23179
|
-
*/
|
|
23180
|
-
const schema = new Schema({ nodes, marks });
|
|
23181
|
-
|
|
23182
|
-
const olDOM = ["ol", 0], ulDOM = ["ul", 0], liDOM = ["li", 0];
|
|
23183
|
-
/**
|
|
23184
|
-
An ordered list [node spec](https://prosemirror.net/docs/ref/#model.NodeSpec). Has a single
|
|
23185
|
-
attribute, `order`, which determines the number at which the list
|
|
23186
|
-
starts counting, and defaults to 1. Represented as an `<ol>`
|
|
23187
|
-
element.
|
|
23188
|
-
*/
|
|
23189
|
-
const orderedList = {
|
|
23190
|
-
attrs: { order: { default: 1 } },
|
|
23191
|
-
parseDOM: [{ tag: "ol", getAttrs(dom) {
|
|
23192
|
-
return { order: dom.hasAttribute("start") ? +dom.getAttribute("start") : 1 };
|
|
23193
|
-
} }],
|
|
23194
|
-
toDOM(node) {
|
|
23195
|
-
return node.attrs.order == 1 ? olDOM : ["ol", { start: node.attrs.order }, 0];
|
|
23196
|
-
}
|
|
23197
|
-
};
|
|
23198
|
-
/**
|
|
23199
|
-
A bullet list node spec, represented in the DOM as `<ul>`.
|
|
23200
|
-
*/
|
|
23201
|
-
const bulletList = {
|
|
23202
|
-
parseDOM: [{ tag: "ul" }],
|
|
23203
|
-
toDOM() { return ulDOM; }
|
|
23204
|
-
};
|
|
23205
|
-
/**
|
|
23206
|
-
A list item (`<li>`) spec.
|
|
23207
|
-
*/
|
|
23208
|
-
const listItem = {
|
|
23209
|
-
parseDOM: [{ tag: "li" }],
|
|
23210
|
-
toDOM() { return liDOM; },
|
|
23211
|
-
defining: true
|
|
23212
|
-
};
|
|
23213
|
-
function add(obj, props) {
|
|
23214
|
-
let copy = {};
|
|
23215
|
-
for (let prop in obj)
|
|
23216
|
-
copy[prop] = obj[prop];
|
|
23217
|
-
for (let prop in props)
|
|
23218
|
-
copy[prop] = props[prop];
|
|
23219
|
-
return copy;
|
|
23220
|
-
}
|
|
23221
|
-
/**
|
|
23222
|
-
Convenience function for adding list-related node types to a map
|
|
23223
|
-
specifying the nodes for a schema. Adds
|
|
23224
|
-
[`orderedList`](https://prosemirror.net/docs/ref/#schema-list.orderedList) as `"ordered_list"`,
|
|
23225
|
-
[`bulletList`](https://prosemirror.net/docs/ref/#schema-list.bulletList) as `"bullet_list"`, and
|
|
23226
|
-
[`listItem`](https://prosemirror.net/docs/ref/#schema-list.listItem) as `"list_item"`.
|
|
23227
|
-
|
|
23228
|
-
`itemContent` determines the content expression for the list items.
|
|
23229
|
-
If you want the commands defined in this module to apply to your
|
|
23230
|
-
list structure, it should have a shape like `"paragraph block*"` or
|
|
23231
|
-
`"paragraph (ordered_list | bullet_list)*"`. `listGroup` can be
|
|
23232
|
-
given to assign a group name to the list node types, for example
|
|
23233
|
-
`"block"`.
|
|
23234
|
-
*/
|
|
23235
|
-
function addListNodes(nodes, itemContent, listGroup) {
|
|
23236
|
-
return nodes.append({
|
|
23237
|
-
ordered_list: add(orderedList, { content: "list_item+", group: listGroup }),
|
|
23238
|
-
bullet_list: add(bulletList, { content: "list_item+", group: listGroup }),
|
|
23239
|
-
list_item: add(listItem, { content: itemContent })
|
|
23240
|
-
});
|
|
23241
|
-
}
|
|
23242
|
-
/**
|
|
23243
|
-
Returns a command function that wraps the selection in a list with
|
|
23244
|
-
the given type an attributes. If `dispatch` is null, only return a
|
|
23245
|
-
value to indicate whether this is possible, but don't actually
|
|
23246
|
-
perform the change.
|
|
23247
|
-
*/
|
|
23248
|
-
function wrapInList(listType, attrs = null) {
|
|
23249
|
-
return function (state, dispatch) {
|
|
23250
|
-
let { $from, $to } = state.selection;
|
|
23251
|
-
let range = $from.blockRange($to), doJoin = false, outerRange = range;
|
|
23252
|
-
if (!range)
|
|
23253
|
-
return false;
|
|
23254
|
-
// This is at the top of an existing list item
|
|
23255
|
-
if (range.depth >= 2 && $from.node(range.depth - 1).type.compatibleContent(listType) && range.startIndex == 0) {
|
|
23256
|
-
// Don't do anything if this is the top of the list
|
|
23257
|
-
if ($from.index(range.depth - 1) == 0)
|
|
23258
|
-
return false;
|
|
23259
|
-
let $insert = state.doc.resolve(range.start - 2);
|
|
23260
|
-
outerRange = new NodeRange($insert, $insert, range.depth);
|
|
23261
|
-
if (range.endIndex < range.parent.childCount)
|
|
23262
|
-
range = new NodeRange($from, state.doc.resolve($to.end(range.depth)), range.depth);
|
|
23263
|
-
doJoin = true;
|
|
23264
|
-
}
|
|
23265
|
-
let wrap = findWrapping(outerRange, listType, attrs, range);
|
|
23266
|
-
if (!wrap)
|
|
23267
|
-
return false;
|
|
23268
|
-
if (dispatch)
|
|
23269
|
-
dispatch(doWrapInList(state.tr, range, wrap, doJoin, listType).scrollIntoView());
|
|
23270
|
-
return true;
|
|
23271
|
-
};
|
|
23272
|
-
}
|
|
23273
|
-
function doWrapInList(tr, range, wrappers, joinBefore, listType) {
|
|
23274
|
-
let content = Fragment.empty;
|
|
23275
|
-
for (let i = wrappers.length - 1; i >= 0; i--)
|
|
23276
|
-
content = Fragment.from(wrappers[i].type.create(wrappers[i].attrs, content));
|
|
23277
|
-
tr.step(new ReplaceAroundStep(range.start - (joinBefore ? 2 : 0), range.end, range.start, range.end, new Slice(content, 0, 0), wrappers.length, true));
|
|
23278
|
-
let found = 0;
|
|
23279
|
-
for (let i = 0; i < wrappers.length; i++)
|
|
23280
|
-
if (wrappers[i].type == listType)
|
|
23281
|
-
found = i + 1;
|
|
23282
|
-
let splitDepth = wrappers.length - found;
|
|
23283
|
-
let splitPos = range.start + wrappers.length - (joinBefore ? 2 : 0), parent = range.parent;
|
|
23284
|
-
for (let i = range.startIndex, e = range.endIndex, first = true; i < e; i++, first = false) {
|
|
23285
|
-
if (!first && canSplit(tr.doc, splitPos, splitDepth)) {
|
|
23286
|
-
tr.split(splitPos, splitDepth);
|
|
23287
|
-
splitPos += 2 * splitDepth;
|
|
23288
|
-
}
|
|
23289
|
-
splitPos += parent.child(i).nodeSize;
|
|
23652
|
+
image: {
|
|
23653
|
+
inline: true,
|
|
23654
|
+
attrs: {
|
|
23655
|
+
src: { validate: "string" },
|
|
23656
|
+
alt: { default: null, validate: "string|null" },
|
|
23657
|
+
title: { default: null, validate: "string|null" }
|
|
23658
|
+
},
|
|
23659
|
+
group: "inline",
|
|
23660
|
+
draggable: true,
|
|
23661
|
+
parseDOM: [{ tag: "img[src]", getAttrs(dom) {
|
|
23662
|
+
return {
|
|
23663
|
+
src: dom.getAttribute("src"),
|
|
23664
|
+
title: dom.getAttribute("title"),
|
|
23665
|
+
alt: dom.getAttribute("alt")
|
|
23666
|
+
};
|
|
23667
|
+
} }],
|
|
23668
|
+
toDOM(node) { let { src, alt, title } = node.attrs; return ["img", { src, alt, title }]; }
|
|
23669
|
+
},
|
|
23670
|
+
/**
|
|
23671
|
+
A hard line break, represented in the DOM as `<br>`.
|
|
23672
|
+
*/
|
|
23673
|
+
hard_break: {
|
|
23674
|
+
inline: true,
|
|
23675
|
+
group: "inline",
|
|
23676
|
+
selectable: false,
|
|
23677
|
+
parseDOM: [{ tag: "br" }],
|
|
23678
|
+
toDOM() { return brDOM; }
|
|
23290
23679
|
}
|
|
23291
|
-
|
|
23292
|
-
|
|
23293
|
-
/**
|
|
23294
|
-
Build a command that splits a non-empty textblock at the top level
|
|
23295
|
-
of a list item by also splitting that list item.
|
|
23296
|
-
*/
|
|
23297
|
-
function splitListItem(itemType, itemAttrs) {
|
|
23298
|
-
return function (state, dispatch) {
|
|
23299
|
-
let { $from, $to, node } = state.selection;
|
|
23300
|
-
if ((node && node.isBlock) || $from.depth < 2 || !$from.sameParent($to))
|
|
23301
|
-
return false;
|
|
23302
|
-
let grandParent = $from.node(-1);
|
|
23303
|
-
if (grandParent.type != itemType)
|
|
23304
|
-
return false;
|
|
23305
|
-
if ($from.parent.content.size == 0 && $from.node(-1).childCount == $from.indexAfter(-1)) {
|
|
23306
|
-
// In an empty block. If this is a nested list, the wrapping
|
|
23307
|
-
// list item should be split. Otherwise, bail out and let next
|
|
23308
|
-
// command handle lifting.
|
|
23309
|
-
if ($from.depth == 3 || $from.node(-3).type != itemType ||
|
|
23310
|
-
$from.index(-2) != $from.node(-2).childCount - 1)
|
|
23311
|
-
return false;
|
|
23312
|
-
if (dispatch) {
|
|
23313
|
-
let wrap = Fragment.empty;
|
|
23314
|
-
let depthBefore = $from.index(-1) ? 1 : $from.index(-2) ? 2 : 3;
|
|
23315
|
-
// Build a fragment containing empty versions of the structure
|
|
23316
|
-
// from the outer list item to the parent node of the cursor
|
|
23317
|
-
for (let d = $from.depth - depthBefore; d >= $from.depth - 3; d--)
|
|
23318
|
-
wrap = Fragment.from($from.node(d).copy(wrap));
|
|
23319
|
-
let depthAfter = $from.indexAfter(-1) < $from.node(-2).childCount ? 1
|
|
23320
|
-
: $from.indexAfter(-2) < $from.node(-3).childCount ? 2 : 3;
|
|
23321
|
-
// Add a second list item with an empty default start node
|
|
23322
|
-
wrap = wrap.append(Fragment.from(itemType.createAndFill()));
|
|
23323
|
-
let start = $from.before($from.depth - (depthBefore - 1));
|
|
23324
|
-
let tr = state.tr.replace(start, $from.after(-depthAfter), new Slice(wrap, 4 - depthBefore, 0));
|
|
23325
|
-
let sel = -1;
|
|
23326
|
-
tr.doc.nodesBetween(start, tr.doc.content.size, (node, pos) => {
|
|
23327
|
-
if (sel > -1)
|
|
23328
|
-
return false;
|
|
23329
|
-
if (node.isTextblock && node.content.size == 0)
|
|
23330
|
-
sel = pos + 1;
|
|
23331
|
-
});
|
|
23332
|
-
if (sel > -1)
|
|
23333
|
-
tr.setSelection(Selection.near(tr.doc.resolve(sel)));
|
|
23334
|
-
dispatch(tr.scrollIntoView());
|
|
23335
|
-
}
|
|
23336
|
-
return true;
|
|
23337
|
-
}
|
|
23338
|
-
let nextType = $to.pos == $from.end() ? grandParent.contentMatchAt(0).defaultType : null;
|
|
23339
|
-
let tr = state.tr.delete($from.pos, $to.pos);
|
|
23340
|
-
let types = nextType ? [null, { type: nextType }] : undefined;
|
|
23341
|
-
if (!canSplit(tr.doc, $from.pos, 2, types))
|
|
23342
|
-
return false;
|
|
23343
|
-
if (dispatch)
|
|
23344
|
-
dispatch(tr.split($from.pos, 2, types).scrollIntoView());
|
|
23345
|
-
return true;
|
|
23346
|
-
};
|
|
23347
|
-
}
|
|
23680
|
+
};
|
|
23681
|
+
const emDOM = ["em", 0], strongDOM = ["strong", 0], codeDOM = ["code", 0];
|
|
23348
23682
|
/**
|
|
23349
|
-
|
|
23350
|
-
a wrapping list.
|
|
23683
|
+
[Specs](https://prosemirror.net/docs/ref/#model.MarkSpec) for the marks in the schema.
|
|
23351
23684
|
*/
|
|
23352
|
-
|
|
23353
|
-
|
|
23354
|
-
|
|
23355
|
-
|
|
23356
|
-
|
|
23357
|
-
|
|
23358
|
-
|
|
23359
|
-
|
|
23360
|
-
|
|
23361
|
-
|
|
23362
|
-
|
|
23363
|
-
|
|
23364
|
-
|
|
23365
|
-
}
|
|
23366
|
-
|
|
23367
|
-
|
|
23368
|
-
|
|
23369
|
-
|
|
23370
|
-
|
|
23371
|
-
|
|
23372
|
-
|
|
23373
|
-
|
|
23374
|
-
|
|
23375
|
-
|
|
23376
|
-
|
|
23377
|
-
|
|
23378
|
-
|
|
23379
|
-
|
|
23380
|
-
|
|
23381
|
-
|
|
23382
|
-
|
|
23383
|
-
|
|
23384
|
-
|
|
23385
|
-
|
|
23386
|
-
|
|
23387
|
-
|
|
23388
|
-
|
|
23389
|
-
|
|
23685
|
+
const marks = {
|
|
23686
|
+
/**
|
|
23687
|
+
A link. Has `href` and `title` attributes. `title`
|
|
23688
|
+
defaults to the empty string. Rendered and parsed as an `<a>`
|
|
23689
|
+
element.
|
|
23690
|
+
*/
|
|
23691
|
+
link: {
|
|
23692
|
+
attrs: {
|
|
23693
|
+
href: { validate: "string" },
|
|
23694
|
+
title: { default: null, validate: "string|null" }
|
|
23695
|
+
},
|
|
23696
|
+
inclusive: false,
|
|
23697
|
+
parseDOM: [{ tag: "a[href]", getAttrs(dom) {
|
|
23698
|
+
return { href: dom.getAttribute("href"), title: dom.getAttribute("title") };
|
|
23699
|
+
} }],
|
|
23700
|
+
toDOM(node) { let { href, title } = node.attrs; return ["a", { href, title }, 0]; }
|
|
23701
|
+
},
|
|
23702
|
+
/**
|
|
23703
|
+
An emphasis mark. Rendered as an `<em>` element. Has parse rules
|
|
23704
|
+
that also match `<i>` and `font-style: italic`.
|
|
23705
|
+
*/
|
|
23706
|
+
em: {
|
|
23707
|
+
parseDOM: [
|
|
23708
|
+
{ tag: "i" }, { tag: "em" },
|
|
23709
|
+
{ style: "font-style=italic" },
|
|
23710
|
+
{ style: "font-style=normal", clearMark: m => m.type.name == "em" }
|
|
23711
|
+
],
|
|
23712
|
+
toDOM() { return emDOM; }
|
|
23713
|
+
},
|
|
23714
|
+
/**
|
|
23715
|
+
A strong mark. Rendered as `<strong>`, parse rules also match
|
|
23716
|
+
`<b>` and `font-weight: bold`.
|
|
23717
|
+
*/
|
|
23718
|
+
strong: {
|
|
23719
|
+
parseDOM: [
|
|
23720
|
+
{ tag: "strong" },
|
|
23721
|
+
// This works around a Google Docs misbehavior where
|
|
23722
|
+
// pasted content will be inexplicably wrapped in `<b>`
|
|
23723
|
+
// tags with a font-weight normal.
|
|
23724
|
+
{ tag: "b", getAttrs: (node) => node.style.fontWeight != "normal" && null },
|
|
23725
|
+
{ style: "font-weight=400", clearMark: m => m.type.name == "strong" },
|
|
23726
|
+
{ style: "font-weight", getAttrs: (value) => /^(bold(er)?|[5-9]\d{2,})$/.test(value) && null },
|
|
23727
|
+
],
|
|
23728
|
+
toDOM() { return strongDOM; }
|
|
23729
|
+
},
|
|
23730
|
+
/**
|
|
23731
|
+
Code font mark. Represented as a `<code>` element.
|
|
23732
|
+
*/
|
|
23733
|
+
code: {
|
|
23734
|
+
code: true,
|
|
23735
|
+
parseDOM: [{ tag: "code" }],
|
|
23736
|
+
toDOM() { return codeDOM; }
|
|
23390
23737
|
}
|
|
23391
|
-
|
|
23392
|
-
if (tr.mapping.map(range.end) != range.start + $start.nodeAfter.nodeSize)
|
|
23393
|
-
return false;
|
|
23394
|
-
let atStart = range.startIndex == 0, atEnd = range.endIndex == list.childCount;
|
|
23395
|
-
let parent = $start.node(-1), indexBefore = $start.index(-1);
|
|
23396
|
-
if (!parent.canReplace(indexBefore + (atStart ? 0 : 1), indexBefore + 1, item.content.append(atEnd ? Fragment.empty : Fragment.from(list))))
|
|
23397
|
-
return false;
|
|
23398
|
-
let start = $start.pos, end = start + item.nodeSize;
|
|
23399
|
-
// Strip off the surrounding list. At the sides where we're not at
|
|
23400
|
-
// the end of the list, the existing list is closed. At sides where
|
|
23401
|
-
// this is the end, it is overwritten to its end.
|
|
23402
|
-
tr.step(new ReplaceAroundStep(start - (atStart ? 1 : 0), end + (atEnd ? 1 : 0), start + 1, end - 1, new Slice((atStart ? Fragment.empty : Fragment.from(list.copy(Fragment.empty)))
|
|
23403
|
-
.append(atEnd ? Fragment.empty : Fragment.from(list.copy(Fragment.empty))), atStart ? 0 : 1, atEnd ? 0 : 1), atStart ? 0 : 1));
|
|
23404
|
-
dispatch(tr.scrollIntoView());
|
|
23405
|
-
return true;
|
|
23406
|
-
}
|
|
23738
|
+
};
|
|
23407
23739
|
/**
|
|
23408
|
-
|
|
23409
|
-
|
|
23740
|
+
This schema roughly corresponds to the document schema used by
|
|
23741
|
+
[CommonMark](http://commonmark.org/), minus the list elements,
|
|
23742
|
+
which are defined in the [`prosemirror-schema-list`](https://prosemirror.net/docs/ref/#schema-list)
|
|
23743
|
+
module.
|
|
23744
|
+
|
|
23745
|
+
To reuse elements from this schema, extend or read from its
|
|
23746
|
+
`spec.nodes` and `spec.marks` [properties](https://prosemirror.net/docs/ref/#model.Schema.spec).
|
|
23410
23747
|
*/
|
|
23411
|
-
|
|
23412
|
-
return function (state, dispatch) {
|
|
23413
|
-
let { $from, $to } = state.selection;
|
|
23414
|
-
let range = $from.blockRange($to, node => node.childCount > 0 && node.firstChild.type == itemType);
|
|
23415
|
-
if (!range)
|
|
23416
|
-
return false;
|
|
23417
|
-
let startIndex = range.startIndex;
|
|
23418
|
-
if (startIndex == 0)
|
|
23419
|
-
return false;
|
|
23420
|
-
let parent = range.parent, nodeBefore = parent.child(startIndex - 1);
|
|
23421
|
-
if (nodeBefore.type != itemType)
|
|
23422
|
-
return false;
|
|
23423
|
-
if (dispatch) {
|
|
23424
|
-
let nestedBefore = nodeBefore.lastChild && nodeBefore.lastChild.type == parent.type;
|
|
23425
|
-
let inner = Fragment.from(nestedBefore ? itemType.create() : null);
|
|
23426
|
-
let slice = new Slice(Fragment.from(itemType.create(null, Fragment.from(parent.type.create(null, inner)))), nestedBefore ? 3 : 1, 0);
|
|
23427
|
-
let before = range.start, after = range.end;
|
|
23428
|
-
dispatch(state.tr.step(new ReplaceAroundStep(before - (nestedBefore ? 3 : 1), after, before, after, slice, 1, true))
|
|
23429
|
-
.scrollIntoView());
|
|
23430
|
-
}
|
|
23431
|
-
return true;
|
|
23432
|
-
};
|
|
23433
|
-
}
|
|
23748
|
+
const schema = new Schema({ nodes, marks });
|
|
23434
23749
|
|
|
23435
23750
|
var base = {
|
|
23436
23751
|
8: "Backspace",
|
|
@@ -25988,6 +26303,64 @@ const createActionBarInteractionPlugin = (menuCommandFactory) => {
|
|
|
25988
26303
|
});
|
|
25989
26304
|
};
|
|
25990
26305
|
|
|
26306
|
+
/**
|
|
26307
|
+
* Finds the depth of the nearest ancestor of a given node type.
|
|
26308
|
+
*
|
|
26309
|
+
* @param $pos - The resolved position in the document.
|
|
26310
|
+
* @param type - The node type to search for.
|
|
26311
|
+
* @returns The depth at which the node is found, or null if not found.
|
|
26312
|
+
*/
|
|
26313
|
+
const findAncestorDepthOfType = ($pos, type) => {
|
|
26314
|
+
for (let depth = $pos.depth; depth > 0; depth--) {
|
|
26315
|
+
if ($pos.node(depth).type === type) {
|
|
26316
|
+
return depth;
|
|
26317
|
+
}
|
|
26318
|
+
}
|
|
26319
|
+
return null;
|
|
26320
|
+
};
|
|
26321
|
+
|
|
26322
|
+
const listKeyHandlerPluginKey = new PluginKey('listKeyHandlerPlugin');
|
|
26323
|
+
/**
|
|
26324
|
+
* Creates a plugin for handling Tab and Shift+Tab inside list items:
|
|
26325
|
+
* Tab indents the item and Shift+Tab outdents it. Enter and Backspace
|
|
26326
|
+
* are handled by the base keymap (splitListItem and joinBackward), which
|
|
26327
|
+
* runs before this plugin.
|
|
26328
|
+
*
|
|
26329
|
+
* @param schema - The document schema
|
|
26330
|
+
* @returns A ProseMirror plugin for handling list-specific key events
|
|
26331
|
+
*/
|
|
26332
|
+
function createListKeyHandlerPlugin(schema) {
|
|
26333
|
+
return new Plugin({
|
|
26334
|
+
key: listKeyHandlerPluginKey,
|
|
26335
|
+
props: {
|
|
26336
|
+
handleKeyDown: (view, event) => {
|
|
26337
|
+
if (event.key !== 'Tab') {
|
|
26338
|
+
return false;
|
|
26339
|
+
}
|
|
26340
|
+
const { state } = view;
|
|
26341
|
+
const { $from } = state.selection;
|
|
26342
|
+
const inListItem = findAncestorDepthOfType($from, schema.nodes.list_item) !==
|
|
26343
|
+
null;
|
|
26344
|
+
if (!inListItem) {
|
|
26345
|
+
return false;
|
|
26346
|
+
}
|
|
26347
|
+
// Inside a list item, Tab and Shift+Tab are always handled
|
|
26348
|
+
// so focus never leaves the editor mid-list, even when the
|
|
26349
|
+
// indent or outdent itself cannot apply.
|
|
26350
|
+
event.preventDefault();
|
|
26351
|
+
if (event.shiftKey) {
|
|
26352
|
+
liftListItem(schema.nodes.list_item)(state, view.dispatch);
|
|
26353
|
+
return true;
|
|
26354
|
+
}
|
|
26355
|
+
// Sinking fails on first items (no preceding sibling to
|
|
26356
|
+
// become the parent); Tab is still swallowed.
|
|
26357
|
+
sinkListItem(schema.nodes.list_item)(state, view.dispatch);
|
|
26358
|
+
return true;
|
|
26359
|
+
},
|
|
26360
|
+
},
|
|
26361
|
+
});
|
|
26362
|
+
}
|
|
26363
|
+
|
|
25991
26364
|
const createHtmlInserter = (view, contentConverter, trigger, dispatchTransaction) => {
|
|
25992
26365
|
const schema = view.state.schema;
|
|
25993
26366
|
return async (input) => {
|
|
@@ -27537,7 +27910,15 @@ function buildEditorSchema(options) {
|
|
|
27537
27910
|
function buildEditorPlugins(options) {
|
|
27538
27911
|
const { schema, menuCommandFactory, contentConverter, language, contentType, triggerCharacters, inlineImages, onNewLinkSelection, onImagePasted, onActiveItemsChange, } = options;
|
|
27539
27912
|
return [
|
|
27540
|
-
|
|
27913
|
+
// exampleSetup binds Shift-Ctrl-8/9 to a plain wrapInList. On
|
|
27914
|
+
// Windows/Linux those are the same physical keys as our
|
|
27915
|
+
// Mod-Shift-8/7 list commands, and exampleSetup's keymap runs
|
|
27916
|
+
// first, so its bindings are suppressed to let the context-aware
|
|
27917
|
+
// list commands own the shortcuts on every platform.
|
|
27918
|
+
...exampleSetup({
|
|
27919
|
+
schema: schema,
|
|
27920
|
+
mapKeys: { 'Shift-Ctrl-8': false, 'Shift-Ctrl-9': false },
|
|
27921
|
+
}),
|
|
27541
27922
|
keymap(menuCommandFactory.buildKeymap()),
|
|
27542
27923
|
createTriggerPlugin(triggerCharacters, contentConverter),
|
|
27543
27924
|
createLinkPlugin(onNewLinkSelection),
|
|
@@ -27545,6 +27926,7 @@ function buildEditorPlugins(options) {
|
|
|
27545
27926
|
createImageViewPlugin(language),
|
|
27546
27927
|
createMenuStateTrackingPlugin(editorMenuTypesArray, menuCommandFactory, onActiveItemsChange),
|
|
27547
27928
|
createActionBarInteractionPlugin(menuCommandFactory),
|
|
27929
|
+
createListKeyHandlerPlugin(schema),
|
|
27548
27930
|
...getTableEditingPlugins(contentType === 'html'),
|
|
27549
27931
|
];
|
|
27550
27932
|
}
|
|
@@ -27732,13 +28114,14 @@ const ProsemirrorAdapter = class {
|
|
|
27732
28114
|
return newItem;
|
|
27733
28115
|
};
|
|
27734
28116
|
this.updateActiveActionBarItems = (activeTypes, allowedTypes) => {
|
|
27735
|
-
|
|
28117
|
+
this.actionBarItems = getTextEditorMenuItems()
|
|
28118
|
+
.map(this.getTranslatedItem)
|
|
28119
|
+
.map((item) => {
|
|
27736
28120
|
if (isItem(item)) {
|
|
27737
|
-
return Object.assign(Object.assign({}, item), { selected: activeTypes[item.value],
|
|
28121
|
+
return Object.assign(Object.assign({}, item), { selected: activeTypes[item.value], disabled: !allowedTypes[item.value] });
|
|
27738
28122
|
}
|
|
27739
28123
|
return item;
|
|
27740
28124
|
});
|
|
27741
|
-
this.actionBarItems = newItems.filter((item) => isItem(item) ? item.allowed : true);
|
|
27742
28125
|
};
|
|
27743
28126
|
this.handleTransaction = (transaction) => {
|
|
27744
28127
|
this.transactionFired = true;
|