@lexical/list 0.44.1-nightly.20260519.0 → 0.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{LexicalList.dev.js → dist/LexicalList.dev.js} +220 -31
- package/{LexicalList.dev.mjs → dist/LexicalList.dev.mjs} +219 -33
- package/{LexicalList.mjs → dist/LexicalList.mjs} +3 -0
- package/{LexicalList.node.mjs → dist/LexicalList.node.mjs} +3 -0
- package/dist/LexicalList.prod.js +9 -0
- package/dist/LexicalList.prod.mjs +9 -0
- package/dist/LexicalListExtension.d.ts +29 -0
- package/dist/ListImportExtension.d.ts +31 -0
- package/{index.d.ts → dist/index.d.ts} +4 -37
- package/dist/registerList.d.ts +20 -0
- package/package.json +33 -17
- package/src/LexicalListExtension.ts +76 -0
- package/src/LexicalListItemNode.ts +724 -0
- package/src/LexicalListNode.ts +393 -0
- package/src/ListImportExtension.ts +238 -0
- package/src/checkList.ts +469 -0
- package/src/formatList.ts +582 -0
- package/src/index.ts +98 -0
- package/src/registerList.ts +231 -0
- package/src/utils.ts +196 -0
- package/LexicalList.prod.js +0 -9
- package/LexicalList.prod.mjs +0 -9
- /package/{LexicalList.js → dist/LexicalList.js} +0 -0
- /package/{LexicalList.js.flow → dist/LexicalList.js.flow} +0 -0
- /package/{LexicalListItemNode.d.ts → dist/LexicalListItemNode.d.ts} +0 -0
- /package/{LexicalListNode.d.ts → dist/LexicalListNode.d.ts} +0 -0
- /package/{checkList.d.ts → dist/checkList.d.ts} +0 -0
- /package/{formatList.d.ts → dist/formatList.d.ts} +0 -0
- /package/{utils.d.ts → dist/utils.d.ts} +0 -0
|
@@ -8,9 +8,10 @@
|
|
|
8
8
|
|
|
9
9
|
'use strict';
|
|
10
10
|
|
|
11
|
-
var extension = require('@lexical/extension');
|
|
12
11
|
var utils = require('@lexical/utils');
|
|
13
12
|
var lexical = require('lexical');
|
|
13
|
+
var extension = require('@lexical/extension');
|
|
14
|
+
var html = require('@lexical/html');
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
@@ -1295,7 +1296,7 @@ function $normalizeChildren(nodes, listNode) {
|
|
|
1295
1296
|
}
|
|
1296
1297
|
return normalizedListItems;
|
|
1297
1298
|
}
|
|
1298
|
-
function isDomChecklist(domNode) {
|
|
1299
|
+
function isDomChecklist$1(domNode) {
|
|
1299
1300
|
if (domNode.getAttribute('__lexicallisttype') === 'check' ||
|
|
1300
1301
|
// is github checklist
|
|
1301
1302
|
domNode.classList.contains('contains-task-list') ||
|
|
@@ -1319,7 +1320,7 @@ function $convertListNode(domNode) {
|
|
|
1319
1320
|
if (isHTMLOListElement(domNode)) {
|
|
1320
1321
|
const start = domNode.start;
|
|
1321
1322
|
node = $createListNode('number', start);
|
|
1322
|
-
} else if (isDomChecklist(domNode)) {
|
|
1323
|
+
} else if (isDomChecklist$1(domNode)) {
|
|
1323
1324
|
node = $createListNode('check');
|
|
1324
1325
|
} else {
|
|
1325
1326
|
node = $createListNode('bullet');
|
|
@@ -1787,33 +1788,13 @@ function $findChildrenEndListItemNode(listItemNode) {
|
|
|
1787
1788
|
}
|
|
1788
1789
|
|
|
1789
1790
|
/**
|
|
1790
|
-
*
|
|
1791
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
1791
1792
|
*
|
|
1792
|
-
*
|
|
1793
|
-
*
|
|
1794
|
-
* Otherwise it will replace its parent with a new ListNode and re-insert the ListItemNode and any previous children.
|
|
1795
|
-
* If the selection's anchor node is not an empty ListItemNode, it will add a new ListNode or merge an existing ListNode,
|
|
1796
|
-
* unless the the node is a leaf node, in which case it will attempt to find a ListNode up the branch and replace it with
|
|
1797
|
-
* a new ListNode, or create a new ListNode at the nearest root/shadow root.
|
|
1798
|
-
* @param editor - The lexical editor.
|
|
1799
|
-
* @param listType - The type of list, "number" | "bullet" | "check".
|
|
1800
|
-
*/
|
|
1801
|
-
function insertList(editor, listType) {
|
|
1802
|
-
editor.update(() => $insertList(listType));
|
|
1803
|
-
}
|
|
1804
|
-
|
|
1805
|
-
/**
|
|
1806
|
-
* @deprecated use {@link $removeList} from an update or command listener.
|
|
1793
|
+
* This source code is licensed under the MIT license found in the
|
|
1794
|
+
* LICENSE file in the root directory of this source tree.
|
|
1807
1795
|
*
|
|
1808
|
-
* Searches for the nearest ancestral ListNode and removes it. If selection is an empty ListItemNode
|
|
1809
|
-
* it will remove the whole list, including the ListItemNode. For each ListItemNode in the ListNode,
|
|
1810
|
-
* removeList will also generate new ParagraphNodes in the removed ListNode's place. Any child node
|
|
1811
|
-
* inside a ListItemNode will be appended to the new ParagraphNodes.
|
|
1812
|
-
* @param editor - The lexical editor.
|
|
1813
1796
|
*/
|
|
1814
|
-
|
|
1815
|
-
editor.update(() => $removeList());
|
|
1816
|
-
}
|
|
1797
|
+
|
|
1817
1798
|
/**
|
|
1818
1799
|
* Configures {@link ListNode}, {@link ListItemNode} and registers
|
|
1819
1800
|
* the strict indent transform if `hasStrictIndent` is true (default false).
|
|
@@ -1839,10 +1820,8 @@ const ListExtension = lexical.defineExtension({
|
|
|
1839
1820
|
});
|
|
1840
1821
|
/**
|
|
1841
1822
|
* Registers checklist functionality for {@link ListNode} and
|
|
1842
|
-
* {@link ListItemNode} with a
|
|
1843
|
-
*
|
|
1844
|
-
* the expected keyboard and mouse interactions for
|
|
1845
|
-
* checkboxes.
|
|
1823
|
+
* {@link ListItemNode} with a `INSERT_CHECK_LIST_COMMAND` listener and
|
|
1824
|
+
* the expected keyboard and mouse interactions for checkboxes.
|
|
1846
1825
|
*/
|
|
1847
1826
|
const CheckListExtension = lexical.defineExtension({
|
|
1848
1827
|
build: (editor, config) => extension.namedSignals(config),
|
|
@@ -1854,6 +1833,213 @@ const CheckListExtension = lexical.defineExtension({
|
|
|
1854
1833
|
register: (editor, config, state) => registerCheckList(editor, state.getOutput())
|
|
1855
1834
|
});
|
|
1856
1835
|
|
|
1836
|
+
/**
|
|
1837
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
1838
|
+
*
|
|
1839
|
+
* This source code is licensed under the MIT license found in the
|
|
1840
|
+
* LICENSE file in the root directory of this source tree.
|
|
1841
|
+
*
|
|
1842
|
+
*/
|
|
1843
|
+
|
|
1844
|
+
|
|
1845
|
+
/**
|
|
1846
|
+
* Mirrors the legacy `isDomChecklist` heuristic from
|
|
1847
|
+
* `@lexical/list`.
|
|
1848
|
+
*/
|
|
1849
|
+
function isDomChecklist(domNode) {
|
|
1850
|
+
return domNode.matches('[__lexicallisttype="check"], .contains-task-list, [data-is-checklist="1"]') || domNode.querySelector(':scope > [aria-checked]') !== null;
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
/**
|
|
1854
|
+
* Lift nested `ListNode`s out of `ListItemNode`s into sibling
|
|
1855
|
+
* `ListItemNode`s (the legacy `$normalizeChildren` shape). Also wraps any
|
|
1856
|
+
* non-`ListItemNode` children in a new `ListItemNode`.
|
|
1857
|
+
*/
|
|
1858
|
+
function $normalizeListChildren(children) {
|
|
1859
|
+
const out = [];
|
|
1860
|
+
for (const child of children) {
|
|
1861
|
+
if ($isListItemNode(child)) {
|
|
1862
|
+
out.push(child);
|
|
1863
|
+
const innerChildren = child.getChildren();
|
|
1864
|
+
if (innerChildren.length > 1) {
|
|
1865
|
+
for (const inner of innerChildren) {
|
|
1866
|
+
if ($isListNode(inner)) {
|
|
1867
|
+
out.push($createListItemNode().append(inner));
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
} else {
|
|
1872
|
+
out.push($createListItemNode().append(child));
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
return out;
|
|
1876
|
+
}
|
|
1877
|
+
const ListRule = html.defineImportRule({
|
|
1878
|
+
$import: (ctx, el) => {
|
|
1879
|
+
let node;
|
|
1880
|
+
if (html.isElementOfTag(el, 'ol')) {
|
|
1881
|
+
node = $createListNode('number', el.start);
|
|
1882
|
+
} else if (isDomChecklist(el)) {
|
|
1883
|
+
node = $createListNode('check');
|
|
1884
|
+
} else {
|
|
1885
|
+
node = $createListNode('bullet');
|
|
1886
|
+
}
|
|
1887
|
+
lexical.$setDirectionFromDOM(node, el);
|
|
1888
|
+
return [node.splice(0, 0, $normalizeListChildren(ctx.$importChildren(el)))];
|
|
1889
|
+
},
|
|
1890
|
+
match: html.sel.tag('ol', 'ul'),
|
|
1891
|
+
name: '@lexical/list/list'
|
|
1892
|
+
});
|
|
1893
|
+
|
|
1894
|
+
/**
|
|
1895
|
+
* Apply formatting from the first child paragraph of `<li>` to the list
|
|
1896
|
+
* item itself, then unwrap that paragraph (Google Docs sets the alignment
|
|
1897
|
+
* of the `<p>` inside the `<li>`). Mirrors the legacy
|
|
1898
|
+
* `setFormatFromChildren`.
|
|
1899
|
+
*/
|
|
1900
|
+
function $liftFormatFromSingleParagraph(listItemNode, children) {
|
|
1901
|
+
if (children.length !== 1) {
|
|
1902
|
+
return children;
|
|
1903
|
+
}
|
|
1904
|
+
const firstChild = children[0];
|
|
1905
|
+
if (lexical.$isParagraphNode(firstChild) && !listItemNode.getFormatType() && firstChild.getFormatType()) {
|
|
1906
|
+
listItemNode.setFormat(firstChild.getFormatType());
|
|
1907
|
+
return firstChild.getChildren();
|
|
1908
|
+
}
|
|
1909
|
+
return children;
|
|
1910
|
+
}
|
|
1911
|
+
const ListItemRule = html.defineImportRule({
|
|
1912
|
+
$import: (ctx, el) => {
|
|
1913
|
+
const ariaChecked = el.getAttribute('aria-checked');
|
|
1914
|
+
const checked = ariaChecked === 'true' ? true : ariaChecked === 'false' ? false : undefined;
|
|
1915
|
+
const node = $createListItemNode(checked);
|
|
1916
|
+
lexical.$setFormatFromDOM(node, el);
|
|
1917
|
+
lexical.$setDirectionFromDOM(node, el);
|
|
1918
|
+
return [node.splice(0, 0, $liftFormatFromSingleParagraph(node, ctx.$importChildren(el)))];
|
|
1919
|
+
},
|
|
1920
|
+
match: html.sel.tag('li'),
|
|
1921
|
+
name: '@lexical/list/li'
|
|
1922
|
+
});
|
|
1923
|
+
function $buildChecklistItem(ctx, el, checkboxOwner) {
|
|
1924
|
+
const checkboxInput = html.isElementOfTag(checkboxOwner, 'input') ? checkboxOwner : checkboxOwner.querySelector('input[type="checkbox"]');
|
|
1925
|
+
if (!checkboxInput || checkboxInput.getAttribute('type') !== 'checkbox') {
|
|
1926
|
+
return [];
|
|
1927
|
+
}
|
|
1928
|
+
const checked = checkboxInput.hasAttribute('checked');
|
|
1929
|
+
const node = $createListItemNode(checked);
|
|
1930
|
+
lexical.$setFormatFromDOM(node, el);
|
|
1931
|
+
lexical.$setDirectionFromDOM(node, el);
|
|
1932
|
+
return [node.splice(0, 0, $liftFormatFromSingleParagraph(node, ctx.$importChildren(el)))];
|
|
1933
|
+
}
|
|
1934
|
+
const TaskListItemRule = html.defineImportRule({
|
|
1935
|
+
$import: (ctx, el, $next) => {
|
|
1936
|
+
const input = el.querySelector(':scope > input[type="checkbox"]');
|
|
1937
|
+
if (!input) {
|
|
1938
|
+
return $next();
|
|
1939
|
+
}
|
|
1940
|
+
return $buildChecklistItem(ctx, el, input);
|
|
1941
|
+
},
|
|
1942
|
+
match: html.sel.tag('li').classAll('task-list-item'),
|
|
1943
|
+
name: '@lexical/list/li-task-list-item'
|
|
1944
|
+
});
|
|
1945
|
+
const JoplinChecklistItemRule = html.defineImportRule({
|
|
1946
|
+
$import: (ctx, el, $next) => {
|
|
1947
|
+
const wrapper = el.querySelector(':scope > .checkbox-wrapper');
|
|
1948
|
+
if (!wrapper) {
|
|
1949
|
+
return $next();
|
|
1950
|
+
}
|
|
1951
|
+
const input = wrapper.querySelector(':scope > input[type="checkbox"]');
|
|
1952
|
+
if (!input) {
|
|
1953
|
+
return $next();
|
|
1954
|
+
}
|
|
1955
|
+
return $buildChecklistItem(ctx, el, input);
|
|
1956
|
+
},
|
|
1957
|
+
match: html.sel.tag('li').classAll('joplin-checkbox'),
|
|
1958
|
+
name: '@lexical/list/li-joplin-checkbox'
|
|
1959
|
+
});
|
|
1960
|
+
|
|
1961
|
+
/**
|
|
1962
|
+
* A {@link ChildSchema} that enforces ListNode invariants: only
|
|
1963
|
+
* `ListItemNode` and (immediately-nested) `ListNode` children are
|
|
1964
|
+
* accepted; runs of other children get wrapped in a fresh
|
|
1965
|
+
* `ListItemNode`.
|
|
1966
|
+
*
|
|
1967
|
+
* @experimental
|
|
1968
|
+
*/
|
|
1969
|
+
const ListSchema = {
|
|
1970
|
+
$accepts: child => $isListItemNode(child) || $isListNode(child),
|
|
1971
|
+
// Inline runs inside a `<ul>`/`<ol>` (e.g. text between two `<li>`s)
|
|
1972
|
+
// become the children of a synthetic `ListItemNode`. `ListItemNode`
|
|
1973
|
+
// is itself a block-level container of inlines, so no intermediate
|
|
1974
|
+
// `ParagraphNode` is needed (and the demoted-paragraph normalization
|
|
1975
|
+
// would strip one anyway).
|
|
1976
|
+
$packageRun: run => [$createListItemNode().splice(0, 0, run)],
|
|
1977
|
+
name: 'ListSchema'
|
|
1978
|
+
};
|
|
1979
|
+
|
|
1980
|
+
/**
|
|
1981
|
+
* Import rules for {@link ListNode} and {@link ListItemNode}, including
|
|
1982
|
+
* GitHub task-list and Joplin checkbox heuristics.
|
|
1983
|
+
*
|
|
1984
|
+
* @experimental
|
|
1985
|
+
*/
|
|
1986
|
+
const ListImportRules = [
|
|
1987
|
+
// More specific rules (class-restricted) must precede the generic `li`
|
|
1988
|
+
// rule so they win the dispatch race (lower array index = higher
|
|
1989
|
+
// priority).
|
|
1990
|
+
TaskListItemRule, JoplinChecklistItemRule, ListRule, ListItemRule];
|
|
1991
|
+
|
|
1992
|
+
/**
|
|
1993
|
+
* Bundles {@link ListImportRules} (plus {@link CoreImportExtension}) into
|
|
1994
|
+
* a single dependency.
|
|
1995
|
+
*
|
|
1996
|
+
* @experimental
|
|
1997
|
+
*/
|
|
1998
|
+
const ListImportExtension = lexical.defineExtension({
|
|
1999
|
+
dependencies: [html.CoreImportExtension, ListExtension, lexical.configExtension(html.DOMImportExtension, {
|
|
2000
|
+
rules: ListImportRules
|
|
2001
|
+
})],
|
|
2002
|
+
name: '@lexical/list/Import'
|
|
2003
|
+
});
|
|
2004
|
+
|
|
2005
|
+
/**
|
|
2006
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2007
|
+
*
|
|
2008
|
+
* This source code is licensed under the MIT license found in the
|
|
2009
|
+
* LICENSE file in the root directory of this source tree.
|
|
2010
|
+
*
|
|
2011
|
+
*/
|
|
2012
|
+
|
|
2013
|
+
|
|
2014
|
+
/**
|
|
2015
|
+
* @deprecated use {@link $insertList} from an update or command listener.
|
|
2016
|
+
*
|
|
2017
|
+
* Inserts a new ListNode. If the selection's anchor node is an empty ListItemNode and is a child of
|
|
2018
|
+
* the root/shadow root, it will replace the ListItemNode with a ListNode and the old ListItemNode.
|
|
2019
|
+
* Otherwise it will replace its parent with a new ListNode and re-insert the ListItemNode and any previous children.
|
|
2020
|
+
* If the selection's anchor node is not an empty ListItemNode, it will add a new ListNode or merge an existing ListNode,
|
|
2021
|
+
* unless the the node is a leaf node, in which case it will attempt to find a ListNode up the branch and replace it with
|
|
2022
|
+
* a new ListNode, or create a new ListNode at the nearest root/shadow root.
|
|
2023
|
+
* @param editor - The lexical editor.
|
|
2024
|
+
* @param listType - The type of list, "number" | "bullet" | "check".
|
|
2025
|
+
*/
|
|
2026
|
+
function insertList(editor, listType) {
|
|
2027
|
+
editor.update(() => $insertList(listType));
|
|
2028
|
+
}
|
|
2029
|
+
|
|
2030
|
+
/**
|
|
2031
|
+
* @deprecated use {@link $removeList} from an update or command listener.
|
|
2032
|
+
*
|
|
2033
|
+
* Searches for the nearest ancestral ListNode and removes it. If selection is an empty ListItemNode
|
|
2034
|
+
* it will remove the whole list, including the ListItemNode. For each ListItemNode in the ListNode,
|
|
2035
|
+
* removeList will also generate new ParagraphNodes in the removed ListNode's place. Any child node
|
|
2036
|
+
* inside a ListItemNode will be appended to the new ParagraphNodes.
|
|
2037
|
+
* @param editor - The lexical editor.
|
|
2038
|
+
*/
|
|
2039
|
+
function removeList(editor) {
|
|
2040
|
+
editor.update(() => $removeList());
|
|
2041
|
+
}
|
|
2042
|
+
|
|
1857
2043
|
exports.$createListItemNode = $createListItemNode;
|
|
1858
2044
|
exports.$createListNode = $createListNode;
|
|
1859
2045
|
exports.$getListDepth = $getListDepth;
|
|
@@ -1867,8 +2053,11 @@ exports.INSERT_CHECK_LIST_COMMAND = INSERT_CHECK_LIST_COMMAND;
|
|
|
1867
2053
|
exports.INSERT_ORDERED_LIST_COMMAND = INSERT_ORDERED_LIST_COMMAND;
|
|
1868
2054
|
exports.INSERT_UNORDERED_LIST_COMMAND = INSERT_UNORDERED_LIST_COMMAND;
|
|
1869
2055
|
exports.ListExtension = ListExtension;
|
|
2056
|
+
exports.ListImportExtension = ListImportExtension;
|
|
2057
|
+
exports.ListImportRules = ListImportRules;
|
|
1870
2058
|
exports.ListItemNode = ListItemNode;
|
|
1871
2059
|
exports.ListNode = ListNode;
|
|
2060
|
+
exports.ListSchema = ListSchema;
|
|
1872
2061
|
exports.REMOVE_LIST_COMMAND = REMOVE_LIST_COMMAND;
|
|
1873
2062
|
exports.UPDATE_LIST_START_COMMAND = UPDATE_LIST_START_COMMAND;
|
|
1874
2063
|
exports.insertList = insertList;
|
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { effect, namedSignals } from '@lexical/extension';
|
|
10
9
|
import { $getNearestNodeOfType, $insertNodeToNearestRootAtCaret, removeClassNamesFromElement, addClassNamesToElement, isHTMLElement as isHTMLElement$1, mergeRegister, $findMatchingParent, calculateZoomLevel } from '@lexical/utils';
|
|
11
|
-
import { $
|
|
10
|
+
import { $getSelection, $isRangeSelection, $isTextNode, $isRootOrShadowRoot, $createParagraphNode, $copyNode, $isElementNode, $isLeafNode, $setPointFromCaret, $normalizeCaret, $getChildCaret, $applyNodeReplacement, ElementNode, buildImportMap, $getSiblingCaret, $rewindSiblingCaret, setDOMStyleFromCSS, isHTMLElement, $isParagraphNode, $setFormatFromDOM, $setDirectionFromDOM, normalizeClassNames, getStyleObjectFromCSS, $createTextNode, createCommand, COMMAND_PRIORITY_LOW, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_UP_COMMAND, KEY_ESCAPE_COMMAND, KEY_SPACE_COMMAND, $getNearestNodeFromDOMNode, KEY_ARROW_LEFT_COMMAND, getNearestEditorFromDOMNode, $addUpdateTag, SKIP_SELECTION_FOCUS_TAG, SKIP_DOM_SELECTION_TAG, $getNodeByKey, INSERT_PARAGRAPH_COMMAND, TextNode, defineExtension, safeCast, configExtension } from 'lexical';
|
|
11
|
+
import { namedSignals, effect } from '@lexical/extension';
|
|
12
|
+
import { CoreImportExtension, DOMImportExtension, defineImportRule, sel, isElementOfTag } from '@lexical/html';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
@@ -1293,7 +1294,7 @@ function $normalizeChildren(nodes, listNode) {
|
|
|
1293
1294
|
}
|
|
1294
1295
|
return normalizedListItems;
|
|
1295
1296
|
}
|
|
1296
|
-
function isDomChecklist(domNode) {
|
|
1297
|
+
function isDomChecklist$1(domNode) {
|
|
1297
1298
|
if (domNode.getAttribute('__lexicallisttype') === 'check' ||
|
|
1298
1299
|
// is github checklist
|
|
1299
1300
|
domNode.classList.contains('contains-task-list') ||
|
|
@@ -1317,7 +1318,7 @@ function $convertListNode(domNode) {
|
|
|
1317
1318
|
if (isHTMLOListElement(domNode)) {
|
|
1318
1319
|
const start = domNode.start;
|
|
1319
1320
|
node = $createListNode('number', start);
|
|
1320
|
-
} else if (isDomChecklist(domNode)) {
|
|
1321
|
+
} else if (isDomChecklist$1(domNode)) {
|
|
1321
1322
|
node = $createListNode('check');
|
|
1322
1323
|
} else {
|
|
1323
1324
|
node = $createListNode('bullet');
|
|
@@ -1785,33 +1786,13 @@ function $findChildrenEndListItemNode(listItemNode) {
|
|
|
1785
1786
|
}
|
|
1786
1787
|
|
|
1787
1788
|
/**
|
|
1788
|
-
*
|
|
1789
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
1789
1790
|
*
|
|
1790
|
-
*
|
|
1791
|
-
*
|
|
1792
|
-
* Otherwise it will replace its parent with a new ListNode and re-insert the ListItemNode and any previous children.
|
|
1793
|
-
* If the selection's anchor node is not an empty ListItemNode, it will add a new ListNode or merge an existing ListNode,
|
|
1794
|
-
* unless the the node is a leaf node, in which case it will attempt to find a ListNode up the branch and replace it with
|
|
1795
|
-
* a new ListNode, or create a new ListNode at the nearest root/shadow root.
|
|
1796
|
-
* @param editor - The lexical editor.
|
|
1797
|
-
* @param listType - The type of list, "number" | "bullet" | "check".
|
|
1798
|
-
*/
|
|
1799
|
-
function insertList(editor, listType) {
|
|
1800
|
-
editor.update(() => $insertList(listType));
|
|
1801
|
-
}
|
|
1802
|
-
|
|
1803
|
-
/**
|
|
1804
|
-
* @deprecated use {@link $removeList} from an update or command listener.
|
|
1791
|
+
* This source code is licensed under the MIT license found in the
|
|
1792
|
+
* LICENSE file in the root directory of this source tree.
|
|
1805
1793
|
*
|
|
1806
|
-
* Searches for the nearest ancestral ListNode and removes it. If selection is an empty ListItemNode
|
|
1807
|
-
* it will remove the whole list, including the ListItemNode. For each ListItemNode in the ListNode,
|
|
1808
|
-
* removeList will also generate new ParagraphNodes in the removed ListNode's place. Any child node
|
|
1809
|
-
* inside a ListItemNode will be appended to the new ParagraphNodes.
|
|
1810
|
-
* @param editor - The lexical editor.
|
|
1811
1794
|
*/
|
|
1812
|
-
|
|
1813
|
-
editor.update(() => $removeList());
|
|
1814
|
-
}
|
|
1795
|
+
|
|
1815
1796
|
/**
|
|
1816
1797
|
* Configures {@link ListNode}, {@link ListItemNode} and registers
|
|
1817
1798
|
* the strict indent transform if `hasStrictIndent` is true (default false).
|
|
@@ -1837,10 +1818,8 @@ const ListExtension = defineExtension({
|
|
|
1837
1818
|
});
|
|
1838
1819
|
/**
|
|
1839
1820
|
* Registers checklist functionality for {@link ListNode} and
|
|
1840
|
-
* {@link ListItemNode} with a
|
|
1841
|
-
*
|
|
1842
|
-
* the expected keyboard and mouse interactions for
|
|
1843
|
-
* checkboxes.
|
|
1821
|
+
* {@link ListItemNode} with a `INSERT_CHECK_LIST_COMMAND` listener and
|
|
1822
|
+
* the expected keyboard and mouse interactions for checkboxes.
|
|
1844
1823
|
*/
|
|
1845
1824
|
const CheckListExtension = defineExtension({
|
|
1846
1825
|
build: (editor, config) => namedSignals(config),
|
|
@@ -1852,4 +1831,211 @@ const CheckListExtension = defineExtension({
|
|
|
1852
1831
|
register: (editor, config, state) => registerCheckList(editor, state.getOutput())
|
|
1853
1832
|
});
|
|
1854
1833
|
|
|
1855
|
-
|
|
1834
|
+
/**
|
|
1835
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
1836
|
+
*
|
|
1837
|
+
* This source code is licensed under the MIT license found in the
|
|
1838
|
+
* LICENSE file in the root directory of this source tree.
|
|
1839
|
+
*
|
|
1840
|
+
*/
|
|
1841
|
+
|
|
1842
|
+
|
|
1843
|
+
/**
|
|
1844
|
+
* Mirrors the legacy `isDomChecklist` heuristic from
|
|
1845
|
+
* `@lexical/list`.
|
|
1846
|
+
*/
|
|
1847
|
+
function isDomChecklist(domNode) {
|
|
1848
|
+
return domNode.matches('[__lexicallisttype="check"], .contains-task-list, [data-is-checklist="1"]') || domNode.querySelector(':scope > [aria-checked]') !== null;
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
/**
|
|
1852
|
+
* Lift nested `ListNode`s out of `ListItemNode`s into sibling
|
|
1853
|
+
* `ListItemNode`s (the legacy `$normalizeChildren` shape). Also wraps any
|
|
1854
|
+
* non-`ListItemNode` children in a new `ListItemNode`.
|
|
1855
|
+
*/
|
|
1856
|
+
function $normalizeListChildren(children) {
|
|
1857
|
+
const out = [];
|
|
1858
|
+
for (const child of children) {
|
|
1859
|
+
if ($isListItemNode(child)) {
|
|
1860
|
+
out.push(child);
|
|
1861
|
+
const innerChildren = child.getChildren();
|
|
1862
|
+
if (innerChildren.length > 1) {
|
|
1863
|
+
for (const inner of innerChildren) {
|
|
1864
|
+
if ($isListNode(inner)) {
|
|
1865
|
+
out.push($createListItemNode().append(inner));
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
} else {
|
|
1870
|
+
out.push($createListItemNode().append(child));
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
return out;
|
|
1874
|
+
}
|
|
1875
|
+
const ListRule = defineImportRule({
|
|
1876
|
+
$import: (ctx, el) => {
|
|
1877
|
+
let node;
|
|
1878
|
+
if (isElementOfTag(el, 'ol')) {
|
|
1879
|
+
node = $createListNode('number', el.start);
|
|
1880
|
+
} else if (isDomChecklist(el)) {
|
|
1881
|
+
node = $createListNode('check');
|
|
1882
|
+
} else {
|
|
1883
|
+
node = $createListNode('bullet');
|
|
1884
|
+
}
|
|
1885
|
+
$setDirectionFromDOM(node, el);
|
|
1886
|
+
return [node.splice(0, 0, $normalizeListChildren(ctx.$importChildren(el)))];
|
|
1887
|
+
},
|
|
1888
|
+
match: sel.tag('ol', 'ul'),
|
|
1889
|
+
name: '@lexical/list/list'
|
|
1890
|
+
});
|
|
1891
|
+
|
|
1892
|
+
/**
|
|
1893
|
+
* Apply formatting from the first child paragraph of `<li>` to the list
|
|
1894
|
+
* item itself, then unwrap that paragraph (Google Docs sets the alignment
|
|
1895
|
+
* of the `<p>` inside the `<li>`). Mirrors the legacy
|
|
1896
|
+
* `setFormatFromChildren`.
|
|
1897
|
+
*/
|
|
1898
|
+
function $liftFormatFromSingleParagraph(listItemNode, children) {
|
|
1899
|
+
if (children.length !== 1) {
|
|
1900
|
+
return children;
|
|
1901
|
+
}
|
|
1902
|
+
const firstChild = children[0];
|
|
1903
|
+
if ($isParagraphNode(firstChild) && !listItemNode.getFormatType() && firstChild.getFormatType()) {
|
|
1904
|
+
listItemNode.setFormat(firstChild.getFormatType());
|
|
1905
|
+
return firstChild.getChildren();
|
|
1906
|
+
}
|
|
1907
|
+
return children;
|
|
1908
|
+
}
|
|
1909
|
+
const ListItemRule = defineImportRule({
|
|
1910
|
+
$import: (ctx, el) => {
|
|
1911
|
+
const ariaChecked = el.getAttribute('aria-checked');
|
|
1912
|
+
const checked = ariaChecked === 'true' ? true : ariaChecked === 'false' ? false : undefined;
|
|
1913
|
+
const node = $createListItemNode(checked);
|
|
1914
|
+
$setFormatFromDOM(node, el);
|
|
1915
|
+
$setDirectionFromDOM(node, el);
|
|
1916
|
+
return [node.splice(0, 0, $liftFormatFromSingleParagraph(node, ctx.$importChildren(el)))];
|
|
1917
|
+
},
|
|
1918
|
+
match: sel.tag('li'),
|
|
1919
|
+
name: '@lexical/list/li'
|
|
1920
|
+
});
|
|
1921
|
+
function $buildChecklistItem(ctx, el, checkboxOwner) {
|
|
1922
|
+
const checkboxInput = isElementOfTag(checkboxOwner, 'input') ? checkboxOwner : checkboxOwner.querySelector('input[type="checkbox"]');
|
|
1923
|
+
if (!checkboxInput || checkboxInput.getAttribute('type') !== 'checkbox') {
|
|
1924
|
+
return [];
|
|
1925
|
+
}
|
|
1926
|
+
const checked = checkboxInput.hasAttribute('checked');
|
|
1927
|
+
const node = $createListItemNode(checked);
|
|
1928
|
+
$setFormatFromDOM(node, el);
|
|
1929
|
+
$setDirectionFromDOM(node, el);
|
|
1930
|
+
return [node.splice(0, 0, $liftFormatFromSingleParagraph(node, ctx.$importChildren(el)))];
|
|
1931
|
+
}
|
|
1932
|
+
const TaskListItemRule = defineImportRule({
|
|
1933
|
+
$import: (ctx, el, $next) => {
|
|
1934
|
+
const input = el.querySelector(':scope > input[type="checkbox"]');
|
|
1935
|
+
if (!input) {
|
|
1936
|
+
return $next();
|
|
1937
|
+
}
|
|
1938
|
+
return $buildChecklistItem(ctx, el, input);
|
|
1939
|
+
},
|
|
1940
|
+
match: sel.tag('li').classAll('task-list-item'),
|
|
1941
|
+
name: '@lexical/list/li-task-list-item'
|
|
1942
|
+
});
|
|
1943
|
+
const JoplinChecklistItemRule = defineImportRule({
|
|
1944
|
+
$import: (ctx, el, $next) => {
|
|
1945
|
+
const wrapper = el.querySelector(':scope > .checkbox-wrapper');
|
|
1946
|
+
if (!wrapper) {
|
|
1947
|
+
return $next();
|
|
1948
|
+
}
|
|
1949
|
+
const input = wrapper.querySelector(':scope > input[type="checkbox"]');
|
|
1950
|
+
if (!input) {
|
|
1951
|
+
return $next();
|
|
1952
|
+
}
|
|
1953
|
+
return $buildChecklistItem(ctx, el, input);
|
|
1954
|
+
},
|
|
1955
|
+
match: sel.tag('li').classAll('joplin-checkbox'),
|
|
1956
|
+
name: '@lexical/list/li-joplin-checkbox'
|
|
1957
|
+
});
|
|
1958
|
+
|
|
1959
|
+
/**
|
|
1960
|
+
* A {@link ChildSchema} that enforces ListNode invariants: only
|
|
1961
|
+
* `ListItemNode` and (immediately-nested) `ListNode` children are
|
|
1962
|
+
* accepted; runs of other children get wrapped in a fresh
|
|
1963
|
+
* `ListItemNode`.
|
|
1964
|
+
*
|
|
1965
|
+
* @experimental
|
|
1966
|
+
*/
|
|
1967
|
+
const ListSchema = {
|
|
1968
|
+
$accepts: child => $isListItemNode(child) || $isListNode(child),
|
|
1969
|
+
// Inline runs inside a `<ul>`/`<ol>` (e.g. text between two `<li>`s)
|
|
1970
|
+
// become the children of a synthetic `ListItemNode`. `ListItemNode`
|
|
1971
|
+
// is itself a block-level container of inlines, so no intermediate
|
|
1972
|
+
// `ParagraphNode` is needed (and the demoted-paragraph normalization
|
|
1973
|
+
// would strip one anyway).
|
|
1974
|
+
$packageRun: run => [$createListItemNode().splice(0, 0, run)],
|
|
1975
|
+
name: 'ListSchema'
|
|
1976
|
+
};
|
|
1977
|
+
|
|
1978
|
+
/**
|
|
1979
|
+
* Import rules for {@link ListNode} and {@link ListItemNode}, including
|
|
1980
|
+
* GitHub task-list and Joplin checkbox heuristics.
|
|
1981
|
+
*
|
|
1982
|
+
* @experimental
|
|
1983
|
+
*/
|
|
1984
|
+
const ListImportRules = [
|
|
1985
|
+
// More specific rules (class-restricted) must precede the generic `li`
|
|
1986
|
+
// rule so they win the dispatch race (lower array index = higher
|
|
1987
|
+
// priority).
|
|
1988
|
+
TaskListItemRule, JoplinChecklistItemRule, ListRule, ListItemRule];
|
|
1989
|
+
|
|
1990
|
+
/**
|
|
1991
|
+
* Bundles {@link ListImportRules} (plus {@link CoreImportExtension}) into
|
|
1992
|
+
* a single dependency.
|
|
1993
|
+
*
|
|
1994
|
+
* @experimental
|
|
1995
|
+
*/
|
|
1996
|
+
const ListImportExtension = defineExtension({
|
|
1997
|
+
dependencies: [CoreImportExtension, ListExtension, configExtension(DOMImportExtension, {
|
|
1998
|
+
rules: ListImportRules
|
|
1999
|
+
})],
|
|
2000
|
+
name: '@lexical/list/Import'
|
|
2001
|
+
});
|
|
2002
|
+
|
|
2003
|
+
/**
|
|
2004
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2005
|
+
*
|
|
2006
|
+
* This source code is licensed under the MIT license found in the
|
|
2007
|
+
* LICENSE file in the root directory of this source tree.
|
|
2008
|
+
*
|
|
2009
|
+
*/
|
|
2010
|
+
|
|
2011
|
+
|
|
2012
|
+
/**
|
|
2013
|
+
* @deprecated use {@link $insertList} from an update or command listener.
|
|
2014
|
+
*
|
|
2015
|
+
* Inserts a new ListNode. If the selection's anchor node is an empty ListItemNode and is a child of
|
|
2016
|
+
* the root/shadow root, it will replace the ListItemNode with a ListNode and the old ListItemNode.
|
|
2017
|
+
* Otherwise it will replace its parent with a new ListNode and re-insert the ListItemNode and any previous children.
|
|
2018
|
+
* If the selection's anchor node is not an empty ListItemNode, it will add a new ListNode or merge an existing ListNode,
|
|
2019
|
+
* unless the the node is a leaf node, in which case it will attempt to find a ListNode up the branch and replace it with
|
|
2020
|
+
* a new ListNode, or create a new ListNode at the nearest root/shadow root.
|
|
2021
|
+
* @param editor - The lexical editor.
|
|
2022
|
+
* @param listType - The type of list, "number" | "bullet" | "check".
|
|
2023
|
+
*/
|
|
2024
|
+
function insertList(editor, listType) {
|
|
2025
|
+
editor.update(() => $insertList(listType));
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
/**
|
|
2029
|
+
* @deprecated use {@link $removeList} from an update or command listener.
|
|
2030
|
+
*
|
|
2031
|
+
* Searches for the nearest ancestral ListNode and removes it. If selection is an empty ListItemNode
|
|
2032
|
+
* it will remove the whole list, including the ListItemNode. For each ListItemNode in the ListNode,
|
|
2033
|
+
* removeList will also generate new ParagraphNodes in the removed ListNode's place. Any child node
|
|
2034
|
+
* inside a ListItemNode will be appended to the new ParagraphNodes.
|
|
2035
|
+
* @param editor - The lexical editor.
|
|
2036
|
+
*/
|
|
2037
|
+
function removeList(editor) {
|
|
2038
|
+
editor.update(() => $removeList());
|
|
2039
|
+
}
|
|
2040
|
+
|
|
2041
|
+
export { $createListItemNode, $createListNode, $getListDepth, $handleListInsertParagraph, $insertList, $isListItemNode, $isListNode, $removeList, CheckListExtension, INSERT_CHECK_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND, ListExtension, ListImportExtension, ListImportRules, ListItemNode, ListNode, ListSchema, REMOVE_LIST_COMMAND, UPDATE_LIST_START_COMMAND, insertList, registerCheckList, registerList, registerListStrictIndentTransform, removeList };
|
|
@@ -22,8 +22,11 @@ export const INSERT_CHECK_LIST_COMMAND = mod.INSERT_CHECK_LIST_COMMAND;
|
|
|
22
22
|
export const INSERT_ORDERED_LIST_COMMAND = mod.INSERT_ORDERED_LIST_COMMAND;
|
|
23
23
|
export const INSERT_UNORDERED_LIST_COMMAND = mod.INSERT_UNORDERED_LIST_COMMAND;
|
|
24
24
|
export const ListExtension = mod.ListExtension;
|
|
25
|
+
export const ListImportExtension = mod.ListImportExtension;
|
|
26
|
+
export const ListImportRules = mod.ListImportRules;
|
|
25
27
|
export const ListItemNode = mod.ListItemNode;
|
|
26
28
|
export const ListNode = mod.ListNode;
|
|
29
|
+
export const ListSchema = mod.ListSchema;
|
|
27
30
|
export const REMOVE_LIST_COMMAND = mod.REMOVE_LIST_COMMAND;
|
|
28
31
|
export const UPDATE_LIST_START_COMMAND = mod.UPDATE_LIST_START_COMMAND;
|
|
29
32
|
export const insertList = mod.insertList;
|
|
@@ -20,8 +20,11 @@ export const INSERT_CHECK_LIST_COMMAND = mod.INSERT_CHECK_LIST_COMMAND;
|
|
|
20
20
|
export const INSERT_ORDERED_LIST_COMMAND = mod.INSERT_ORDERED_LIST_COMMAND;
|
|
21
21
|
export const INSERT_UNORDERED_LIST_COMMAND = mod.INSERT_UNORDERED_LIST_COMMAND;
|
|
22
22
|
export const ListExtension = mod.ListExtension;
|
|
23
|
+
export const ListImportExtension = mod.ListImportExtension;
|
|
24
|
+
export const ListImportRules = mod.ListImportRules;
|
|
23
25
|
export const ListItemNode = mod.ListItemNode;
|
|
24
26
|
export const ListNode = mod.ListNode;
|
|
27
|
+
export const ListSchema = mod.ListSchema;
|
|
25
28
|
export const REMOVE_LIST_COMMAND = mod.REMOVE_LIST_COMMAND;
|
|
26
29
|
export const UPDATE_LIST_START_COMMAND = mod.UPDATE_LIST_START_COMMAND;
|
|
27
30
|
export const insertList = mod.insertList;
|