@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.
@@ -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
- * @deprecated use {@link $insertList} from an update or command listener.
1791
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
1791
1792
  *
1792
- * Inserts a new ListNode. If the selection's anchor node is an empty ListItemNode and is a child of
1793
- * the root/shadow root, it will replace the ListItemNode with a ListNode and the old ListItemNode.
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
- function removeList(editor) {
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
- * {@link INSERT_CHECK_LIST_COMMAND} listener and
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 { $copyNode, $getSelection, $isRangeSelection, $isRootOrShadowRoot, $createParagraphNode, $isElementNode, $isLeafNode, $setPointFromCaret, $normalizeCaret, $getChildCaret, $isTextNode, ElementNode, buildImportMap, $getSiblingCaret, $rewindSiblingCaret, setDOMStyleFromCSS, isHTMLElement, $isParagraphNode, $applyNodeReplacement, $setFormatFromDOM, $setDirectionFromDOM, normalizeClassNames, getStyleObjectFromCSS, $createTextNode, COMMAND_PRIORITY_LOW, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_UP_COMMAND, KEY_ESCAPE_COMMAND, KEY_SPACE_COMMAND, $getNearestNodeFromDOMNode, KEY_ARROW_LEFT_COMMAND, createCommand, getNearestEditorFromDOMNode, $addUpdateTag, SKIP_SELECTION_FOCUS_TAG, SKIP_DOM_SELECTION_TAG, defineExtension, safeCast, $getNodeByKey, INSERT_PARAGRAPH_COMMAND, TextNode } from 'lexical';
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
- * @deprecated use {@link $insertList} from an update or command listener.
1789
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
1789
1790
  *
1790
- * Inserts a new ListNode. If the selection's anchor node is an empty ListItemNode and is a child of
1791
- * the root/shadow root, it will replace the ListItemNode with a ListNode and the old ListItemNode.
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
- function removeList(editor) {
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
- * {@link INSERT_CHECK_LIST_COMMAND} listener and
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
- export { $createListItemNode, $createListNode, $getListDepth, $handleListInsertParagraph, $insertList, $isListItemNode, $isListNode, $removeList, CheckListExtension, INSERT_CHECK_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND, ListExtension, ListItemNode, ListNode, REMOVE_LIST_COMMAND, UPDATE_LIST_START_COMMAND, insertList, registerCheckList, registerList, registerListStrictIndentTransform, removeList };
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;