diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c9ecca8b11..3d185739801 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,50 @@ +## v0.18.0 (2024-09-26) + +- lexical-markdown set shouldMergeAdjacentLines default to be false (#6660) Sherry +- lexical-link Bug Fix Removing a link from descendants (#6656) Sergey Gorbachev +- lexical-playground Bug Fix Correct ExcalidrawNode DOM handling for proper resizing (#6657) neysanfoo +- lexical-markdown Refactor allows omitting certain properties from TextMatchTransformers, adds jsdocs (#6651) Alessio Gravili +- lexical-table Bug Fix Fix crash in deleteCellHandler (#6650) Bob Ippolito +- lexical-playground Bug Fix empty code block not focused (#6649) Sherry +- lexical-markdown shouldMergeAdjacentLines as an option (#6642) Sherry +- lexical-code Bug Fix Annotate lexicalcode as having side-effects for Prism (#6652) Bob Ippolito +- lexical-reactlexical-playground Bug Fix Workaround for yjs disconnect race in React StrictMode (#6644) Bob Ippolito +- Allow exporting a document fragment from the exportDOM function (#6641) Tom Kubt +- Grammar Issue - Repeated Word (#6643) Adam Pugh +- lexical-tablelexical-playground Feature Add column widths to TableNode (#6625) Patrick Moody +- lexical-playgroundExcalidrawNode Bug Fix Preserve Excalidraw image dimensions after resizing (#6634) neysanfoo +- lexical-table Bug Table formatting and styling not persisting for empty cells (#6626) Katsia +- lexical-markdown Fix normalize markdown in convertFromMarkdownString to comply with CommonMark spec (2nd try) (#6629) Germn Jabloski +- lexical-playground Bug Fix Fix table rowcolumn index when resizing merged cells (#6630) Patrick Moody +- Revert lexical-markdown Fix normalize markdown in convertFromMarkdownString to comply with CommonMark spec (#6608) (#6627) Sherry +- lexical-react Refactor Ensure disconnect is called after connection is established in useYjsCollaboration (#6619) smworld01 +- CI tag flaky test (#6620) Sherry +- address micromatch vulnerability (#6616) Sherry +- Bug Fix Fix issue where selecting a cell then dragging outside of table would not select entire table (#6579) Mo +- lexical-markdown Breaking Change rename multilineElement to multiline-element (#6617) Sherry +- lexical Chore Add more helpful invariants to applyNodeReplacement (#6567) Bob Ippolito +- lexical-markdown Fix normalize markdown in convertFromMarkdownString to comply with CommonMark spec (#6608) Germn Jabloski +- Expose getStyleObjectFromCss in lexicalselection (#6612) Melissa Freiser +- lexical-markdown update markdown flow api (#6615) Sherry +- lexical-playground Fix Poll Option not clickable at some place after checked state (#6609) Vinay Kushwaha +- Multiple update tags (#6507) Gerard Rovira +- lexical-codebreaking change Bug Fix explicitly import instead of window. to support code nodes in nodejs (#6562) Nadine Nguyen +- lexical-tablelexical-playground Bug Fix Fix merged cell related edge cases (#6607) Bob Ippolito +- Fix issue where cmdctrl left arrow after a tab character would cause exception (#6588) Mo +- Address svelte vulnerability (#6603) Sherry +- lexical-markdownbreaking change Feature multiline markdown transformers mdx support (#6530) Alessio Gravili +- lexical-table Fix Table Cut Event Handling (#6596) Ivaylo Pavlov +- lexicallexical-overflow Refactor simplified removeText and insertText rewrite (part 1) (#6456) Germn Jabloski +- Fix test results CI path on Windows (#6585) Mo +- lexical-listlexical-react Refactor Create registerList Function Separate from React Shared Utils (#6560) River +- lexical-react Fix incorrect addition of empty cells on table paste (#6578) Shubhanker Srivastava +- lexical-table feat Add row striping (#6547) Ivaylo Pavlov +- Feature Check undeclared dependencies in build (#6574) Bob Ippolito +- lexical-react menu positioning Unrevert PR6510 but with gating (#6566) Sherry +- lexical-table Bug Fix Add lexicalclipboard as a direct dependency of lexicaltable (#6571) Bob Ippolito +- v0.17.1 (#6559) Ivaylo Pavlov +- v0.17.1 Lexical GitHub Actions Bot + ## v0.17.1 (2024-08-26) - lexical-playground Bug Fix fix comment timestamps (#6555) Ira Hopkinson diff --git a/examples/react-plain-text/package.json b/examples/react-plain-text/package.json index 8b6a1e32f47..2ca57f3e88a 100644 --- a/examples/react-plain-text/package.json +++ b/examples/react-plain-text/package.json @@ -1,7 +1,7 @@ { "name": "@lexical/react-plain-text-example", "private": true, - "version": "0.17.1", + "version": "0.18.0", "type": "module", "scripts": { "dev": "vite", @@ -9,8 +9,8 @@ "preview": "vite preview" }, "dependencies": { - "@lexical/react": "0.17.1", - "lexical": "0.17.1", + "@lexical/react": "0.18.0", + "lexical": "0.18.0", "react": "^18.2.0", "react-dom": "^18.2.0" }, diff --git a/examples/react-rich-collab/package.json b/examples/react-rich-collab/package.json index 7ea1556935a..07ad5f31dca 100644 --- a/examples/react-rich-collab/package.json +++ b/examples/react-rich-collab/package.json @@ -1,7 +1,7 @@ { "name": "@lexical/react-rich-collab-example", "private": true, - "version": "0.17.1", + "version": "0.18.0", "type": "module", "scripts": { "dev": "vite", @@ -12,9 +12,9 @@ "server:webrtc": "cross-env HOST=localhost PORT=1235 npx y-webrtc" }, "dependencies": { - "@lexical/react": "0.17.1", - "@lexical/yjs": "0.17.1", - "lexical": "0.17.1", + "@lexical/react": "0.18.0", + "@lexical/yjs": "0.18.0", + "lexical": "0.18.0", "react": "^18.2.0", "react-dom": "^18.2.0", "y-webrtc": "^10.3.0", diff --git a/examples/react-rich/package.json b/examples/react-rich/package.json index 001d080f988..f16d62b0f75 100644 --- a/examples/react-rich/package.json +++ b/examples/react-rich/package.json @@ -1,7 +1,7 @@ { "name": "@lexical/react-rich-example", "private": true, - "version": "0.17.1", + "version": "0.18.0", "type": "module", "scripts": { "dev": "vite", @@ -9,8 +9,8 @@ "preview": "vite preview" }, "dependencies": { - "@lexical/react": "0.17.1", - "lexical": "0.17.1", + "@lexical/react": "0.18.0", + "lexical": "0.18.0", "react": "^18.2.0", "react-dom": "^18.2.0" }, diff --git a/examples/react-table/package.json b/examples/react-table/package.json index 7b1d3f90e0b..19b01d84ed8 100644 --- a/examples/react-table/package.json +++ b/examples/react-table/package.json @@ -1,7 +1,7 @@ { "name": "@lexical/react-table-example", "private": true, - "version": "0.17.1", + "version": "0.18.0", "type": "module", "scripts": { "dev": "vite", @@ -9,8 +9,8 @@ "preview": "vite preview" }, "dependencies": { - "@lexical/react": "0.17.1", - "lexical": "0.17.1", + "@lexical/react": "0.18.0", + "lexical": "0.18.0", "react": "^18.2.0", "react-dom": "^18.2.0" }, diff --git a/examples/vanilla-js-plugin/package.json b/examples/vanilla-js-plugin/package.json index 3fcc072d9e6..46c9eef6358 100644 --- a/examples/vanilla-js-plugin/package.json +++ b/examples/vanilla-js-plugin/package.json @@ -1,7 +1,7 @@ { "name": "@lexical/vanilla-js-plugin-example", "private": true, - "version": "0.17.1", + "version": "0.18.0", "type": "module", "scripts": { "dev": "vite", @@ -9,12 +9,12 @@ "preview": "vite preview" }, "dependencies": { - "@lexical/dragon": "0.17.1", - "@lexical/history": "0.17.1", - "@lexical/rich-text": "0.17.1", - "@lexical/utils": "0.17.1", + "@lexical/dragon": "0.18.0", + "@lexical/history": "0.18.0", + "@lexical/rich-text": "0.18.0", + "@lexical/utils": "0.18.0", "emoji-datasource-facebook": "15.1.2", - "lexical": "0.17.1" + "lexical": "0.18.0" }, "devDependencies": { "typescript": "^5.2.2", diff --git a/examples/vanilla-js/package.json b/examples/vanilla-js/package.json index 93f90b943eb..bda715c2e58 100644 --- a/examples/vanilla-js/package.json +++ b/examples/vanilla-js/package.json @@ -1,7 +1,7 @@ { "name": "@lexical/vanilla-js-example", "private": true, - "version": "0.17.1", + "version": "0.18.0", "type": "module", "scripts": { "dev": "vite", @@ -9,11 +9,11 @@ "preview": "vite preview" }, "dependencies": { - "@lexical/dragon": "0.17.1", - "@lexical/history": "0.17.1", - "@lexical/rich-text": "0.17.1", - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/dragon": "0.18.0", + "@lexical/history": "0.18.0", + "@lexical/rich-text": "0.18.0", + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" }, "devDependencies": { "typescript": "^5.2.2", diff --git a/package-lock.json b/package-lock.json index d8fb0f29353..216f1c67140 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@lexical/monorepo", - "version": "0.17.1", + "version": "0.18.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@lexical/monorepo", - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "workspaces": [ "packages/*" @@ -36268,28 +36268,28 @@ } }, "packages/lexical": { - "version": "0.17.1", + "version": "0.18.0", "license": "MIT" }, "packages/lexical-clipboard": { "name": "@lexical/clipboard", - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@lexical/html": "0.17.1", - "@lexical/list": "0.17.1", - "@lexical/selection": "0.17.1", - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/html": "0.18.0", + "@lexical/list": "0.18.0", + "@lexical/selection": "0.18.0", + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" } }, "packages/lexical-code": { "name": "@lexical/code", - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@lexical/utils": "0.17.1", - "lexical": "0.17.1", + "@lexical/utils": "0.18.0", + "lexical": "0.18.0", "prismjs": "^1.27.0" }, "devDependencies": { @@ -36298,7 +36298,7 @@ }, "packages/lexical-devtools": { "name": "@lexical/devtools", - "version": "0.17.1", + "version": "0.18.0", "hasInstallScript": true, "dependencies": { "@chakra-ui/react": "^2.8.2", @@ -36315,12 +36315,12 @@ "devDependencies": { "@babel/plugin-transform-flow-strip-types": "^7.24.7", "@babel/preset-react": "^7.24.7", - "@lexical/devtools-core": "0.17.1", + "@lexical/devtools-core": "0.18.0", "@rollup/plugin-babel": "^6.0.4", "@types/react": "^18.2.46", "@types/react-dom": "^18.2.18", "@vitejs/plugin-react": "^4.2.1", - "lexical": "0.17.1", + "lexical": "0.18.0", "typescript": "^5.4.5", "vite": "^5.2.2", "wxt": "^0.17.0" @@ -36328,15 +36328,15 @@ }, "packages/lexical-devtools-core": { "name": "@lexical/devtools-core", - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@lexical/html": "0.17.1", - "@lexical/link": "0.17.1", - "@lexical/mark": "0.17.1", - "@lexical/table": "0.17.1", - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/html": "0.18.0", + "@lexical/link": "0.18.0", + "@lexical/mark": "0.18.0", + "@lexical/table": "0.18.0", + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" }, "peerDependencies": { "react": ">=17.x", @@ -36345,15 +36345,15 @@ }, "packages/lexical-dragon": { "name": "@lexical/dragon", - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "dependencies": { - "lexical": "0.17.1" + "lexical": "0.18.0" } }, "packages/lexical-eslint-plugin": { "name": "@lexical/eslint-plugin", - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "devDependencies": { "@types/eslint": "^8.56.9" @@ -36364,136 +36364,136 @@ }, "packages/lexical-file": { "name": "@lexical/file", - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "dependencies": { - "lexical": "0.17.1" + "lexical": "0.18.0" } }, "packages/lexical-hashtag": { "name": "@lexical/hashtag", - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" } }, "packages/lexical-headless": { "name": "@lexical/headless", - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "dependencies": { - "lexical": "0.17.1" + "lexical": "0.18.0" } }, "packages/lexical-history": { "name": "@lexical/history", - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" } }, "packages/lexical-html": { "name": "@lexical/html", - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@lexical/selection": "0.17.1", - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/selection": "0.18.0", + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" } }, "packages/lexical-link": { "name": "@lexical/link", - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" } }, "packages/lexical-list": { "name": "@lexical/list", - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" } }, "packages/lexical-mark": { "name": "@lexical/mark", - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" } }, "packages/lexical-markdown": { "name": "@lexical/markdown", - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@lexical/code": "0.17.1", - "@lexical/link": "0.17.1", - "@lexical/list": "0.17.1", - "@lexical/rich-text": "0.17.1", - "@lexical/text": "0.17.1", - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/code": "0.18.0", + "@lexical/link": "0.18.0", + "@lexical/list": "0.18.0", + "@lexical/rich-text": "0.18.0", + "@lexical/text": "0.18.0", + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" } }, "packages/lexical-offset": { "name": "@lexical/offset", - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "dependencies": { - "lexical": "0.17.1" + "lexical": "0.18.0" } }, "packages/lexical-overflow": { "name": "@lexical/overflow", - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "dependencies": { - "lexical": "0.17.1" + "lexical": "0.18.0" } }, "packages/lexical-plain-text": { "name": "@lexical/plain-text", - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@lexical/clipboard": "0.17.1", - "@lexical/selection": "0.17.1", - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/clipboard": "0.18.0", + "@lexical/selection": "0.18.0", + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" } }, "packages/lexical-playground": { - "version": "0.17.1", + "version": "0.18.0", "dependencies": { "@excalidraw/excalidraw": "^0.17.0", - "@lexical/clipboard": "0.17.1", - "@lexical/code": "0.17.1", - "@lexical/file": "0.17.1", - "@lexical/hashtag": "0.17.1", - "@lexical/link": "0.17.1", - "@lexical/list": "0.17.1", - "@lexical/mark": "0.17.1", - "@lexical/overflow": "0.17.1", - "@lexical/plain-text": "0.17.1", - "@lexical/react": "0.17.1", - "@lexical/rich-text": "0.17.1", - "@lexical/selection": "0.17.1", - "@lexical/table": "0.17.1", - "@lexical/utils": "0.17.1", + "@lexical/clipboard": "0.18.0", + "@lexical/code": "0.18.0", + "@lexical/file": "0.18.0", + "@lexical/hashtag": "0.18.0", + "@lexical/link": "0.18.0", + "@lexical/list": "0.18.0", + "@lexical/mark": "0.18.0", + "@lexical/overflow": "0.18.0", + "@lexical/plain-text": "0.18.0", + "@lexical/react": "0.18.0", + "@lexical/rich-text": "0.18.0", + "@lexical/selection": "0.18.0", + "@lexical/table": "0.18.0", + "@lexical/utils": "0.18.0", "katex": "^0.16.10", - "lexical": "0.17.1", + "lexical": "0.18.0", "lodash-es": "^4.17.21", "prettier": "^2.3.2", "react": "^18.2.0", @@ -36516,28 +36516,28 @@ }, "packages/lexical-react": { "name": "@lexical/react", - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@lexical/clipboard": "0.17.1", - "@lexical/code": "0.17.1", - "@lexical/devtools-core": "0.17.1", - "@lexical/dragon": "0.17.1", - "@lexical/hashtag": "0.17.1", - "@lexical/history": "0.17.1", - "@lexical/link": "0.17.1", - "@lexical/list": "0.17.1", - "@lexical/mark": "0.17.1", - "@lexical/markdown": "0.17.1", - "@lexical/overflow": "0.17.1", - "@lexical/plain-text": "0.17.1", - "@lexical/rich-text": "0.17.1", - "@lexical/selection": "0.17.1", - "@lexical/table": "0.17.1", - "@lexical/text": "0.17.1", - "@lexical/utils": "0.17.1", - "@lexical/yjs": "0.17.1", - "lexical": "0.17.1", + "@lexical/clipboard": "0.18.0", + "@lexical/code": "0.18.0", + "@lexical/devtools-core": "0.18.0", + "@lexical/dragon": "0.18.0", + "@lexical/hashtag": "0.18.0", + "@lexical/history": "0.18.0", + "@lexical/link": "0.18.0", + "@lexical/list": "0.18.0", + "@lexical/mark": "0.18.0", + "@lexical/markdown": "0.18.0", + "@lexical/overflow": "0.18.0", + "@lexical/plain-text": "0.18.0", + "@lexical/rich-text": "0.18.0", + "@lexical/selection": "0.18.0", + "@lexical/table": "0.18.0", + "@lexical/text": "0.18.0", + "@lexical/utils": "0.18.0", + "@lexical/yjs": "0.18.0", + "lexical": "0.18.0", "react-error-boundary": "^3.1.4" }, "peerDependencies": { @@ -36547,55 +36547,55 @@ }, "packages/lexical-rich-text": { "name": "@lexical/rich-text", - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@lexical/clipboard": "0.17.1", - "@lexical/selection": "0.17.1", - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/clipboard": "0.18.0", + "@lexical/selection": "0.18.0", + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" } }, "packages/lexical-selection": { "name": "@lexical/selection", - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "dependencies": { - "lexical": "0.17.1" + "lexical": "0.18.0" } }, "packages/lexical-table": { "name": "@lexical/table", - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@lexical/clipboard": "0.17.1", - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/clipboard": "0.18.0", + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" } }, "packages/lexical-text": { "name": "@lexical/text", - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "dependencies": { - "lexical": "0.17.1" + "lexical": "0.18.0" } }, "packages/lexical-utils": { "name": "@lexical/utils", - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@lexical/list": "0.17.1", - "@lexical/selection": "0.17.1", - "@lexical/table": "0.17.1", - "lexical": "0.17.1" + "@lexical/list": "0.18.0", + "@lexical/selection": "0.18.0", + "@lexical/table": "0.18.0", + "lexical": "0.18.0" } }, "packages/lexical-website": { "name": "@lexical/website", - "version": "0.17.1", + "version": "0.18.0", "dependencies": { "@docusaurus/core": "^3.3.2", "@docusaurus/preset-classic": "^3.3.2", @@ -36624,12 +36624,12 @@ }, "packages/lexical-yjs": { "name": "@lexical/yjs", - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@lexical/offset": "0.17.1", - "@lexical/selection": "0.17.1", - "lexical": "0.17.1" + "@lexical/offset": "0.18.0", + "@lexical/selection": "0.18.0", + "lexical": "0.18.0" }, "peerDependencies": { "yjs": ">=13.5.22" @@ -36662,10 +36662,10 @@ } }, "packages/shared": { - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "dependencies": { - "lexical": "0.17.1" + "lexical": "0.18.0" } } }, @@ -40990,19 +40990,19 @@ "@lexical/clipboard": { "version": "file:packages/lexical-clipboard", "requires": { - "@lexical/html": "0.17.1", - "@lexical/list": "0.17.1", - "@lexical/selection": "0.17.1", - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/html": "0.18.0", + "@lexical/list": "0.18.0", + "@lexical/selection": "0.18.0", + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" } }, "@lexical/code": { "version": "file:packages/lexical-code", "requires": { - "@lexical/utils": "0.17.1", + "@lexical/utils": "0.18.0", "@types/prismjs": "^1.26.0", - "lexical": "0.17.1", + "lexical": "0.18.0", "prismjs": "^1.27.0" } }, @@ -41014,7 +41014,7 @@ "@chakra-ui/react": "^2.8.2", "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", - "@lexical/devtools-core": "0.17.1", + "@lexical/devtools-core": "0.18.0", "@rollup/plugin-babel": "^6.0.4", "@types/react": "^18.2.46", "@types/react-dom": "^18.2.18", @@ -41023,7 +41023,7 @@ "@webext-pegasus/store-zustand": "^0.3.0", "@webext-pegasus/transport": "^0.3.0", "framer-motion": "^11.1.5", - "lexical": "0.17.1", + "lexical": "0.18.0", "react": "^18.2.0", "react-dom": "^18.2.0", "typescript": "^5.4.5", @@ -41035,18 +41035,18 @@ "@lexical/devtools-core": { "version": "file:packages/lexical-devtools-core", "requires": { - "@lexical/html": "0.17.1", - "@lexical/link": "0.17.1", - "@lexical/mark": "0.17.1", - "@lexical/table": "0.17.1", - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/html": "0.18.0", + "@lexical/link": "0.18.0", + "@lexical/mark": "0.18.0", + "@lexical/table": "0.18.0", + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" } }, "@lexical/dragon": { "version": "file:packages/lexical-dragon", "requires": { - "lexical": "0.17.1" + "lexical": "0.18.0" } }, "@lexical/eslint-plugin": { @@ -41058,152 +41058,152 @@ "@lexical/file": { "version": "file:packages/lexical-file", "requires": { - "lexical": "0.17.1" + "lexical": "0.18.0" } }, "@lexical/hashtag": { "version": "file:packages/lexical-hashtag", "requires": { - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" } }, "@lexical/headless": { "version": "file:packages/lexical-headless", "requires": { - "lexical": "0.17.1" + "lexical": "0.18.0" } }, "@lexical/history": { "version": "file:packages/lexical-history", "requires": { - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" } }, "@lexical/html": { "version": "file:packages/lexical-html", "requires": { - "@lexical/selection": "0.17.1", - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/selection": "0.18.0", + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" } }, "@lexical/link": { "version": "file:packages/lexical-link", "requires": { - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" } }, "@lexical/list": { "version": "file:packages/lexical-list", "requires": { - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" } }, "@lexical/mark": { "version": "file:packages/lexical-mark", "requires": { - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" } }, "@lexical/markdown": { "version": "file:packages/lexical-markdown", "requires": { - "@lexical/code": "0.17.1", - "@lexical/link": "0.17.1", - "@lexical/list": "0.17.1", - "@lexical/rich-text": "0.17.1", - "@lexical/text": "0.17.1", - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/code": "0.18.0", + "@lexical/link": "0.18.0", + "@lexical/list": "0.18.0", + "@lexical/rich-text": "0.18.0", + "@lexical/text": "0.18.0", + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" } }, "@lexical/offset": { "version": "file:packages/lexical-offset", "requires": { - "lexical": "0.17.1" + "lexical": "0.18.0" } }, "@lexical/overflow": { "version": "file:packages/lexical-overflow", "requires": { - "lexical": "0.17.1" + "lexical": "0.18.0" } }, "@lexical/plain-text": { "version": "file:packages/lexical-plain-text", "requires": { - "@lexical/clipboard": "0.17.1", - "@lexical/selection": "0.17.1", - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/clipboard": "0.18.0", + "@lexical/selection": "0.18.0", + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" } }, "@lexical/react": { "version": "file:packages/lexical-react", "requires": { - "@lexical/clipboard": "0.17.1", - "@lexical/code": "0.17.1", - "@lexical/devtools-core": "0.17.1", - "@lexical/dragon": "0.17.1", - "@lexical/hashtag": "0.17.1", - "@lexical/history": "0.17.1", - "@lexical/link": "0.17.1", - "@lexical/list": "0.17.1", - "@lexical/mark": "0.17.1", - "@lexical/markdown": "0.17.1", - "@lexical/overflow": "0.17.1", - "@lexical/plain-text": "0.17.1", - "@lexical/rich-text": "0.17.1", - "@lexical/selection": "0.17.1", - "@lexical/table": "0.17.1", - "@lexical/text": "0.17.1", - "@lexical/utils": "0.17.1", - "@lexical/yjs": "0.17.1", - "lexical": "0.17.1", + "@lexical/clipboard": "0.18.0", + "@lexical/code": "0.18.0", + "@lexical/devtools-core": "0.18.0", + "@lexical/dragon": "0.18.0", + "@lexical/hashtag": "0.18.0", + "@lexical/history": "0.18.0", + "@lexical/link": "0.18.0", + "@lexical/list": "0.18.0", + "@lexical/mark": "0.18.0", + "@lexical/markdown": "0.18.0", + "@lexical/overflow": "0.18.0", + "@lexical/plain-text": "0.18.0", + "@lexical/rich-text": "0.18.0", + "@lexical/selection": "0.18.0", + "@lexical/table": "0.18.0", + "@lexical/text": "0.18.0", + "@lexical/utils": "0.18.0", + "@lexical/yjs": "0.18.0", + "lexical": "0.18.0", "react-error-boundary": "^3.1.4" } }, "@lexical/rich-text": { "version": "file:packages/lexical-rich-text", "requires": { - "@lexical/clipboard": "0.17.1", - "@lexical/selection": "0.17.1", - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/clipboard": "0.18.0", + "@lexical/selection": "0.18.0", + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" } }, "@lexical/selection": { "version": "file:packages/lexical-selection", "requires": { - "lexical": "0.17.1" + "lexical": "0.18.0" } }, "@lexical/table": { "version": "file:packages/lexical-table", "requires": { - "@lexical/clipboard": "0.17.1", - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/clipboard": "0.18.0", + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" } }, "@lexical/text": { "version": "file:packages/lexical-text", "requires": { - "lexical": "0.17.1" + "lexical": "0.18.0" } }, "@lexical/utils": { "version": "file:packages/lexical-utils", "requires": { - "@lexical/list": "0.17.1", - "@lexical/selection": "0.17.1", - "@lexical/table": "0.17.1", - "lexical": "0.17.1" + "@lexical/list": "0.18.0", + "@lexical/selection": "0.18.0", + "@lexical/table": "0.18.0", + "lexical": "0.18.0" } }, "@lexical/website": { @@ -41235,9 +41235,9 @@ "@lexical/yjs": { "version": "file:packages/lexical-yjs", "requires": { - "@lexical/offset": "0.17.1", - "@lexical/selection": "0.17.1", - "lexical": "0.17.1" + "@lexical/offset": "0.18.0", + "@lexical/selection": "0.18.0", + "lexical": "0.18.0" } }, "@mdx-js/mdx": { @@ -52957,26 +52957,26 @@ "@babel/plugin-transform-flow-strip-types": "^7.24.7", "@babel/preset-react": "^7.24.7", "@excalidraw/excalidraw": "^0.17.0", - "@lexical/clipboard": "0.17.1", - "@lexical/code": "0.17.1", - "@lexical/file": "0.17.1", - "@lexical/hashtag": "0.17.1", - "@lexical/link": "0.17.1", - "@lexical/list": "0.17.1", - "@lexical/mark": "0.17.1", - "@lexical/overflow": "0.17.1", - "@lexical/plain-text": "0.17.1", - "@lexical/react": "0.17.1", - "@lexical/rich-text": "0.17.1", - "@lexical/selection": "0.17.1", - "@lexical/table": "0.17.1", - "@lexical/utils": "0.17.1", + "@lexical/clipboard": "0.18.0", + "@lexical/code": "0.18.0", + "@lexical/file": "0.18.0", + "@lexical/hashtag": "0.18.0", + "@lexical/link": "0.18.0", + "@lexical/list": "0.18.0", + "@lexical/mark": "0.18.0", + "@lexical/overflow": "0.18.0", + "@lexical/plain-text": "0.18.0", + "@lexical/react": "0.18.0", + "@lexical/rich-text": "0.18.0", + "@lexical/selection": "0.18.0", + "@lexical/table": "0.18.0", + "@lexical/utils": "0.18.0", "@rollup/plugin-babel": "^6.0.4", "@rollup/plugin-commonjs": "^25.0.7", "@types/lodash-es": "^4.14.182", "@vitejs/plugin-react": "^4.2.1", "katex": "^0.16.10", - "lexical": "0.17.1", + "lexical": "0.18.0", "lodash-es": "^4.17.21", "prettier": "^2.3.2", "react": "^18.2.0", @@ -58858,7 +58858,7 @@ "shared": { "version": "file:packages/shared", "requires": { - "lexical": "0.17.1" + "lexical": "0.18.0" } }, "shebang-command": { diff --git a/package.json b/package.json index 5e088f09a9d..1c646ed711c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@lexical/monorepo", "description": "Lexical is an extensible text editor framework that provides excellent reliability, accessibility and performance.", - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "private": true, "workspaces": [ diff --git a/packages/lexical-clipboard/package.json b/packages/lexical-clipboard/package.json index e5dc942e4a3..397e51c1034 100644 --- a/packages/lexical-clipboard/package.json +++ b/packages/lexical-clipboard/package.json @@ -9,15 +9,15 @@ "paste" ], "license": "MIT", - "version": "0.17.1", + "version": "0.18.0", "main": "LexicalClipboard.js", "types": "index.d.ts", "dependencies": { - "@lexical/html": "0.17.1", - "@lexical/list": "0.17.1", - "@lexical/selection": "0.17.1", - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/html": "0.18.0", + "@lexical/list": "0.18.0", + "@lexical/selection": "0.18.0", + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" }, "repository": { "type": "git", diff --git a/packages/lexical-code/package.json b/packages/lexical-code/package.json index 89dd18cadee..91331220c16 100644 --- a/packages/lexical-code/package.json +++ b/packages/lexical-code/package.json @@ -8,12 +8,12 @@ "code" ], "license": "MIT", - "version": "0.17.1", + "version": "0.18.0", "main": "LexicalCode.js", "types": "index.d.ts", "dependencies": { - "@lexical/utils": "0.17.1", - "lexical": "0.17.1", + "@lexical/utils": "0.18.0", + "lexical": "0.18.0", "prismjs": "^1.27.0" }, "repository": { diff --git a/packages/lexical-devtools-core/package.json b/packages/lexical-devtools-core/package.json index c830cfca468..ecec8abf35e 100644 --- a/packages/lexical-devtools-core/package.json +++ b/packages/lexical-devtools-core/package.json @@ -8,16 +8,16 @@ "utils" ], "license": "MIT", - "version": "0.17.1", + "version": "0.18.0", "main": "LexicalDevtoolsCore.js", "types": "index.d.ts", "dependencies": { - "@lexical/html": "0.17.1", - "@lexical/link": "0.17.1", - "@lexical/mark": "0.17.1", - "@lexical/table": "0.17.1", - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/html": "0.18.0", + "@lexical/link": "0.18.0", + "@lexical/mark": "0.18.0", + "@lexical/table": "0.18.0", + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" }, "peerDependencies": { "react": ">=17.x", diff --git a/packages/lexical-devtools/package.json b/packages/lexical-devtools/package.json index 4addb5ec3f7..fb2d1963112 100644 --- a/packages/lexical-devtools/package.json +++ b/packages/lexical-devtools/package.json @@ -2,7 +2,7 @@ "name": "@lexical/devtools", "description": "Lexical DevTools browser extension", "private": true, - "version": "0.17.1", + "version": "0.18.0", "type": "module", "scripts": { "dev": "wxt", @@ -41,12 +41,12 @@ "devDependencies": { "@babel/plugin-transform-flow-strip-types": "^7.24.7", "@babel/preset-react": "^7.24.7", - "@lexical/devtools-core": "0.17.1", + "@lexical/devtools-core": "0.18.0", "@rollup/plugin-babel": "^6.0.4", "@types/react": "^18.2.46", "@types/react-dom": "^18.2.18", "@vitejs/plugin-react": "^4.2.1", - "lexical": "0.17.1", + "lexical": "0.18.0", "typescript": "^5.4.5", "vite": "^5.2.2", "wxt": "^0.17.0" diff --git a/packages/lexical-dragon/package.json b/packages/lexical-dragon/package.json index d1a881dc738..d3bc8eb181f 100644 --- a/packages/lexical-dragon/package.json +++ b/packages/lexical-dragon/package.json @@ -9,7 +9,7 @@ "accessibility" ], "license": "MIT", - "version": "0.17.1", + "version": "0.18.0", "main": "LexicalDragon.js", "types": "index.d.ts", "repository": { @@ -37,6 +37,6 @@ } }, "dependencies": { - "lexical": "0.17.1" + "lexical": "0.18.0" } } diff --git a/packages/lexical-eslint-plugin/package.json b/packages/lexical-eslint-plugin/package.json index 331a6f96a57..cdcd24aede3 100644 --- a/packages/lexical-eslint-plugin/package.json +++ b/packages/lexical-eslint-plugin/package.json @@ -8,7 +8,7 @@ "lexical", "editor" ], - "version": "0.17.1", + "version": "0.18.0", "license": "MIT", "repository": { "type": "git", diff --git a/packages/lexical-file/package.json b/packages/lexical-file/package.json index cbf65c7db66..6da57dffc2f 100644 --- a/packages/lexical-file/package.json +++ b/packages/lexical-file/package.json @@ -10,7 +10,7 @@ "export" ], "license": "MIT", - "version": "0.17.1", + "version": "0.18.0", "main": "LexicalFile.js", "types": "index.d.ts", "repository": { @@ -38,6 +38,6 @@ } }, "dependencies": { - "lexical": "0.17.1" + "lexical": "0.18.0" } } diff --git a/packages/lexical-hashtag/package.json b/packages/lexical-hashtag/package.json index 3937e76a996..d86b7edc3b3 100644 --- a/packages/lexical-hashtag/package.json +++ b/packages/lexical-hashtag/package.json @@ -8,12 +8,12 @@ "hashtag" ], "license": "MIT", - "version": "0.17.1", + "version": "0.18.0", "main": "LexicalHashtag.js", "types": "index.d.ts", "dependencies": { - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" }, "repository": { "type": "git", diff --git a/packages/lexical-headless/package.json b/packages/lexical-headless/package.json index 196f562a1bb..de0ff4fde95 100644 --- a/packages/lexical-headless/package.json +++ b/packages/lexical-headless/package.json @@ -8,7 +8,7 @@ "headless" ], "license": "MIT", - "version": "0.17.1", + "version": "0.18.0", "main": "LexicalHeadless.js", "types": "index.d.ts", "repository": { @@ -36,6 +36,6 @@ } }, "dependencies": { - "lexical": "0.17.1" + "lexical": "0.18.0" } } diff --git a/packages/lexical-history/package.json b/packages/lexical-history/package.json index 98c4b920508..d703a8d73df 100644 --- a/packages/lexical-history/package.json +++ b/packages/lexical-history/package.json @@ -8,12 +8,12 @@ "history" ], "license": "MIT", - "version": "0.17.1", + "version": "0.18.0", "main": "LexicalHistory.js", "types": "index.d.ts", "dependencies": { - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" }, "repository": { "type": "git", diff --git a/packages/lexical-html/package.json b/packages/lexical-html/package.json index 756af25c0b7..2bfca39ac9d 100644 --- a/packages/lexical-html/package.json +++ b/packages/lexical-html/package.json @@ -8,7 +8,7 @@ "html" ], "license": "MIT", - "version": "0.17.1", + "version": "0.18.0", "main": "LexicalHtml.js", "types": "index.d.ts", "repository": { @@ -17,9 +17,9 @@ "directory": "packages/lexical-html" }, "dependencies": { - "@lexical/selection": "0.17.1", - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/selection": "0.18.0", + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" }, "module": "LexicalHtml.mjs", "sideEffects": false, diff --git a/packages/lexical-link/package.json b/packages/lexical-link/package.json index 488f1a06c51..95ca67265d4 100644 --- a/packages/lexical-link/package.json +++ b/packages/lexical-link/package.json @@ -8,12 +8,12 @@ "link" ], "license": "MIT", - "version": "0.17.1", + "version": "0.18.0", "main": "LexicalLink.js", "types": "index.d.ts", "dependencies": { - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" }, "repository": { "type": "git", diff --git a/packages/lexical-link/src/index.ts b/packages/lexical-link/src/index.ts index fe2b9757048..1ddb4dcec47 100644 --- a/packages/lexical-link/src/index.ts +++ b/packages/lexical-link/src/index.ts @@ -18,7 +18,11 @@ import type { SerializedElementNode, } from 'lexical'; -import {addClassNamesToElement, isHTMLAnchorElement} from '@lexical/utils'; +import { + $findMatchingParent, + addClassNamesToElement, + isHTMLAnchorElement, +} from '@lexical/utils'; import { $applyNodeReplacement, $getSelection, @@ -495,16 +499,20 @@ export function $toggleLink( if (url === null) { // Remove LinkNodes nodes.forEach((node) => { - const parent = node.getParent(); + const parentLink = $findMatchingParent( + node, + (parent): parent is LinkNode => + !$isAutoLinkNode(parent) && $isLinkNode(parent), + ); - if (!$isAutoLinkNode(parent) && $isLinkNode(parent)) { - const children = parent.getChildren(); + if (parentLink) { + const children = parentLink.getChildren(); for (let i = 0; i < children.length; i++) { - parent.insertBefore(children[i]); + parentLink.insertBefore(children[i]); } - parent.remove(); + parentLink.remove(); } }); } else { diff --git a/packages/lexical-list/package.json b/packages/lexical-list/package.json index 6f2d51154a3..6ec34d721cf 100644 --- a/packages/lexical-list/package.json +++ b/packages/lexical-list/package.json @@ -8,12 +8,12 @@ "list" ], "license": "MIT", - "version": "0.17.1", + "version": "0.18.0", "main": "LexicalList.js", "types": "index.d.ts", "dependencies": { - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" }, "repository": { "type": "git", diff --git a/packages/lexical-mark/package.json b/packages/lexical-mark/package.json index 75209308dc0..f8d600d721b 100644 --- a/packages/lexical-mark/package.json +++ b/packages/lexical-mark/package.json @@ -8,12 +8,12 @@ "mark" ], "license": "MIT", - "version": "0.17.1", + "version": "0.18.0", "main": "LexicalMark.js", "types": "index.d.ts", "dependencies": { - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" }, "repository": { "type": "git", diff --git a/packages/lexical-markdown/package.json b/packages/lexical-markdown/package.json index 879ee1e14cc..02daab66555 100644 --- a/packages/lexical-markdown/package.json +++ b/packages/lexical-markdown/package.json @@ -8,17 +8,17 @@ "markdown" ], "license": "MIT", - "version": "0.17.1", + "version": "0.18.0", "main": "LexicalMarkdown.js", "types": "index.d.ts", "dependencies": { - "@lexical/code": "0.17.1", - "@lexical/link": "0.17.1", - "@lexical/list": "0.17.1", - "@lexical/rich-text": "0.17.1", - "@lexical/text": "0.17.1", - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/code": "0.18.0", + "@lexical/link": "0.18.0", + "@lexical/list": "0.18.0", + "@lexical/rich-text": "0.18.0", + "@lexical/text": "0.18.0", + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" }, "repository": { "type": "git", diff --git a/packages/lexical-markdown/src/MarkdownTransformers.ts b/packages/lexical-markdown/src/MarkdownTransformers.ts index bb83ad0af10..b724b3ba0ca 100644 --- a/packages/lexical-markdown/src/MarkdownTransformers.ts +++ b/packages/lexical-markdown/src/MarkdownTransformers.ts @@ -550,7 +550,7 @@ export const LINK: TextMatchTransformer = { export function normalizeMarkdown( input: string, - shouldMergeAdjacentLines = true, + shouldMergeAdjacentLines = false, ): string { const lines = input.split('\n'); let inCodeBlock = false; diff --git a/packages/lexical-markdown/src/__tests__/unit/LexicalMarkdown.test.ts b/packages/lexical-markdown/src/__tests__/unit/LexicalMarkdown.test.ts index c7c9874bb2f..d4c1a90148a 100644 --- a/packages/lexical-markdown/src/__tests__/unit/LexicalMarkdown.test.ts +++ b/packages/lexical-markdown/src/__tests__/unit/LexicalMarkdown.test.ts @@ -100,6 +100,7 @@ describe('Markdown', () => { // Multiline paragraphs: https://spec.commonmark.org/dingus/?text=Hello%0Aworld%0A! html: '

Helloworld!

', md: ['Hello', 'world', '!'].join('\n'), + shouldMergeAdjacentLines: true, skipExport: true, }, { @@ -125,6 +126,7 @@ describe('Markdown', () => { // Multiline list items: https://spec.commonmark.org/dingus/?text=-%20Hello%0A-%20world%0A!%0A! html: '', md: '- Hello\n- world\n!\n!', + shouldMergeAdjacentLines: true, skipExport: true, }, { @@ -314,6 +316,7 @@ describe('Markdown', () => { // https://spec.commonmark.org/dingus/?text=%3E%20Hello%0Aworld%0A! html: '
Helloworld!
', md: '> Hello\nworld\n!', + shouldMergeAdjacentLines: true, skipExport: true, }, { @@ -332,11 +335,13 @@ describe('Markdown', () => { customTransformers: [MDX_HTML_TRANSFORMER], html: '

Some HTML in mdx:

From HTML: Some Text
', md: 'Some HTML in mdx:\n\nSome Text', + shouldMergeAdjacentLines: true, }, { customTransformers: [MDX_HTML_TRANSFORMER], html: '

Some HTML in mdx:

From HTML: Line 1Some Text
', md: 'Some HTML in mdx:\n\nLine 1\nSome Text', + shouldMergeAdjacentLines: true, skipExport: true, }, ]; @@ -448,7 +453,7 @@ describe('Markdown', () => { } }); -describe('normalizeMarkdown', () => { +describe('normalizeMarkdown - shouldMergeAdjacentLines = true', () => { it('should combine lines separated by a single \n unless they are in a codeblock', () => { const markdown = ` A1 @@ -482,7 +487,7 @@ E2 E3 `; - expect(normalizeMarkdown(markdown)).toBe(` + expect(normalizeMarkdown(markdown, true)).toBe(` A1A2 A3 @@ -519,6 +524,84 @@ E3 | --- | --- | | c | d | `; - expect(normalizeMarkdown(markdown)).toBe(markdown); + expect(normalizeMarkdown(markdown, true)).toBe(markdown); + }); +}); + +describe('normalizeMarkdown - shouldMergeAdjacentLines = false', () => { + it('should not combine lines separated by a single \n', () => { + const markdown = ` +A1 +A2 + +A3 + +\`\`\`md +B1 +B2 + +B3 +\`\`\` + +C1 +C2 + +C3 + +\`\`\`js +D1 +D2 + +D3 +\`\`\` + +\`\`\`single line code\`\`\` + +E1 +E2 + +E3 +`; + expect(normalizeMarkdown(markdown, false)).toBe(` +A1 +A2 + +A3 + +\`\`\`md +B1 +B2 + +B3 +\`\`\` + +C1 +C2 + +C3 + +\`\`\`js +D1 +D2 + +D3 +\`\`\` + +\`\`\`single line code\`\`\` + +E1 +E2 + +E3 +`); + }); + + it('tables', () => { + const markdown = ` +| a | b | +| --- | --- | +| c | d | +`; + expect(normalizeMarkdown(markdown, false)).toBe(markdown); }); }); diff --git a/packages/lexical-markdown/src/index.ts b/packages/lexical-markdown/src/index.ts index 671b56e5e4e..06bd85dd492 100644 --- a/packages/lexical-markdown/src/index.ts +++ b/packages/lexical-markdown/src/index.ts @@ -85,7 +85,7 @@ function $convertFromMarkdownString( transformers: Array = TRANSFORMERS, node?: ElementNode, shouldPreserveNewLines = false, - shouldMergeAdjacentLines = true, + shouldMergeAdjacentLines = false, ): void { const sanitizedMarkdown = shouldPreserveNewLines ? markdown diff --git a/packages/lexical-offset/package.json b/packages/lexical-offset/package.json index 7af6c012272..d61f8d0b70d 100644 --- a/packages/lexical-offset/package.json +++ b/packages/lexical-offset/package.json @@ -8,7 +8,7 @@ "offset" ], "license": "MIT", - "version": "0.17.1", + "version": "0.18.0", "main": "LexicalOffset.js", "types": "index.d.ts", "repository": { @@ -36,6 +36,6 @@ } }, "dependencies": { - "lexical": "0.17.1" + "lexical": "0.18.0" } } diff --git a/packages/lexical-overflow/package.json b/packages/lexical-overflow/package.json index 8c5da5a1a63..74ed5fa03a6 100644 --- a/packages/lexical-overflow/package.json +++ b/packages/lexical-overflow/package.json @@ -8,7 +8,7 @@ "overflow" ], "license": "MIT", - "version": "0.17.1", + "version": "0.18.0", "main": "LexicalOverflow.js", "types": "index.d.ts", "repository": { @@ -36,6 +36,6 @@ } }, "dependencies": { - "lexical": "0.17.1" + "lexical": "0.18.0" } } diff --git a/packages/lexical-plain-text/package.json b/packages/lexical-plain-text/package.json index 215158accc6..e316bfddd67 100644 --- a/packages/lexical-plain-text/package.json +++ b/packages/lexical-plain-text/package.json @@ -7,7 +7,7 @@ "plain-text" ], "license": "MIT", - "version": "0.17.1", + "version": "0.18.0", "main": "LexicalPlainText.js", "types": "index.d.ts", "repository": { @@ -35,9 +35,9 @@ } }, "dependencies": { - "@lexical/clipboard": "0.17.1", - "@lexical/selection": "0.17.1", - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/clipboard": "0.18.0", + "@lexical/selection": "0.18.0", + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" } } diff --git a/packages/lexical-playground/__tests__/e2e/Markdown.spec.mjs b/packages/lexical-playground/__tests__/e2e/Markdown.spec.mjs index 505b038ce28..b434d62be55 100644 --- a/packages/lexical-playground/__tests__/e2e/Markdown.spec.mjs +++ b/packages/lexical-playground/__tests__/e2e/Markdown.spec.mjs @@ -1310,6 +1310,7 @@ const IMPORTED_MARKDOWN_HTML = html` bold italic strikethrough text, +
@@ -1407,7 +1408,9 @@ const IMPORTED_MARKDOWN_HTML = html` dir="ltr"> Blockquotes text goes here
- And secondline after + And second +
+ line after
- - And can be nested and multiline as well - + And can be nested +
+ and multiline as well diff --git a/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs b/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs index 0624d80d30f..fa5d8574235 100644 --- a/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs +++ b/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs @@ -1026,6 +1026,205 @@ test.describe.parallel('Tables', () => { }, ); + test(`Can style on empty table cells and paragraphs with no text`, async ({ + page, + isPlainText, + isCollab, + }) => { + await initialize({isCollab, page}); + test.skip(isPlainText); + + await focusEditor(page); + await insertTable(page, 2, 3); + await selectAll(page); + + // Apply style on empty table + await clickSelectors(page, ['.bold']); + + // Add text after applying styles + await click(page, 'div[contenteditable="true"] p:first-of-type'); + await page.keyboard.type('abc'); + + await click(page, 'th p:first-of-type'); + await fillTablePartiallyWithText(page); + + // Check that the character styles are applied. + await assertHTML( + page, + html` +

abc

+ + + + + + + + + + + + + + + + +
+

a

+
+

bb

+
+

cc

+
+

d

+
+

e

+
+

f

+
+


+ `, + html` +

abc

+ + + + + + + + + + + + + + + + +
+

a

+
+

bb

+
+

cc

+
+

d

+
+

e

+
+

f

+
+


+ `, + {ignoreClasses: true}, + ); + }); + + test(`Align selection style for table cells`, async ({ + page, + isPlainText, + isCollab, + }) => { + await initialize({isCollab, page}); + test.skip(isPlainText); + + await focusEditor(page); + await insertTable(page, 2, 3); + + // Add text in bold to first cell + await click(page, 'th p:first-of-type'); + await page.keyboard.type('a'); + await page.keyboard.down('Shift'); + await page.keyboard.press('ArrowLeft'); + await page.keyboard.up('Shift'); + await clickSelectors(page, ['.bold']); + + // Apply bold style to whole table + // Bold style shouldn't be applied to any paragraphs and removed from all cells + await selectAll(page); + await clickSelectors(page, ['.bold']); + + // Add text after applying styles + await click(page, 'div[contenteditable="true"] p:first-of-type'); + await page.keyboard.type('abc'); + + await click(page, 'th p:first-of-type'); + await fillTablePartiallyWithText(page); + + // None of the paragraphs have style applied + await assertHTML( + page, + html` +

abc

+ + + + + + + + + + + + + + + + +
+

aa

+
+

bb

+
+

cc

+
+

d

+
+

e

+
+

f

+
+


+ `, + html` +

abc

+ + + + + + + + + + + + + + + + +
+

aa

+
+

bb

+
+

cc

+
+

d

+
+

e

+
+

f

+
+


+ `, + {ignoreClasses: true}, + ); + }); + test( `Can copy + paste (internal) using Table selection`, { diff --git a/packages/lexical-playground/package.json b/packages/lexical-playground/package.json index cd07bf6e8da..c2cafc71468 100644 --- a/packages/lexical-playground/package.json +++ b/packages/lexical-playground/package.json @@ -1,6 +1,6 @@ { "name": "lexical-playground", - "version": "0.17.1", + "version": "0.18.0", "private": true, "type": "module", "scripts": { @@ -12,22 +12,22 @@ }, "dependencies": { "@excalidraw/excalidraw": "^0.17.0", - "@lexical/clipboard": "0.17.1", - "@lexical/code": "0.17.1", - "@lexical/file": "0.17.1", - "@lexical/hashtag": "0.17.1", - "@lexical/link": "0.17.1", - "@lexical/list": "0.17.1", - "@lexical/mark": "0.17.1", - "@lexical/overflow": "0.17.1", - "@lexical/plain-text": "0.17.1", - "@lexical/react": "0.17.1", - "@lexical/rich-text": "0.17.1", - "@lexical/selection": "0.17.1", - "@lexical/table": "0.17.1", - "@lexical/utils": "0.17.1", + "@lexical/clipboard": "0.18.0", + "@lexical/code": "0.18.0", + "@lexical/file": "0.18.0", + "@lexical/hashtag": "0.18.0", + "@lexical/link": "0.18.0", + "@lexical/list": "0.18.0", + "@lexical/mark": "0.18.0", + "@lexical/overflow": "0.18.0", + "@lexical/plain-text": "0.18.0", + "@lexical/react": "0.18.0", + "@lexical/rich-text": "0.18.0", + "@lexical/selection": "0.18.0", + "@lexical/table": "0.18.0", + "@lexical/utils": "0.18.0", "katex": "^0.16.10", - "lexical": "0.17.1", + "lexical": "0.18.0", "lodash-es": "^4.17.21", "prettier": "^2.3.2", "react": "^18.2.0", diff --git a/packages/lexical-playground/src/plugins/ToolbarPlugin/index.tsx b/packages/lexical-playground/src/plugins/ToolbarPlugin/index.tsx index b32d841c7ec..7ce9abbdbcc 100644 --- a/packages/lexical-playground/src/plugins/ToolbarPlugin/index.tsx +++ b/packages/lexical-playground/src/plugins/ToolbarPlugin/index.tsx @@ -578,14 +578,6 @@ export default function ToolbarPlugin({ const elementKey = element.getKey(); const elementDOM = activeEditor.getElementByKey(elementKey); - // Update text format - setIsBold(selection.hasFormat('bold')); - setIsItalic(selection.hasFormat('italic')); - setIsUnderline(selection.hasFormat('underline')); - setIsStrikethrough(selection.hasFormat('strikethrough')); - setIsSubscript(selection.hasFormat('subscript')); - setIsSuperscript(selection.hasFormat('superscript')); - setIsCode(selection.hasFormat('code')); setIsRTL($isParentElementRTL(selection)); // Update links @@ -665,6 +657,15 @@ export default function ToolbarPlugin({ ); } if ($isRangeSelection(selection) || $isTableSelection(selection)) { + // Update text format + setIsBold(selection.hasFormat('bold')); + setIsItalic(selection.hasFormat('italic')); + setIsUnderline(selection.hasFormat('underline')); + setIsStrikethrough(selection.hasFormat('strikethrough')); + setIsSubscript(selection.hasFormat('subscript')); + setIsSuperscript(selection.hasFormat('superscript')); + setIsCode(selection.hasFormat('code')); + setFontSize( $getSelectionStyleValueForProperty(selection, 'font-size', '15px'), ); diff --git a/packages/lexical-react/package.json b/packages/lexical-react/package.json index 0e8f9788c79..6360d26502f 100644 --- a/packages/lexical-react/package.json +++ b/packages/lexical-react/package.json @@ -8,27 +8,27 @@ "rich-text" ], "license": "MIT", - "version": "0.17.1", + "version": "0.18.0", "dependencies": { - "@lexical/clipboard": "0.17.1", - "@lexical/code": "0.17.1", - "@lexical/devtools-core": "0.17.1", - "@lexical/dragon": "0.17.1", - "@lexical/hashtag": "0.17.1", - "@lexical/history": "0.17.1", - "@lexical/link": "0.17.1", - "@lexical/list": "0.17.1", - "@lexical/mark": "0.17.1", - "@lexical/markdown": "0.17.1", - "@lexical/overflow": "0.17.1", - "@lexical/plain-text": "0.17.1", - "@lexical/rich-text": "0.17.1", - "@lexical/selection": "0.17.1", - "@lexical/table": "0.17.1", - "@lexical/text": "0.17.1", - "@lexical/utils": "0.17.1", - "@lexical/yjs": "0.17.1", - "lexical": "0.17.1", + "@lexical/clipboard": "0.18.0", + "@lexical/code": "0.18.0", + "@lexical/devtools-core": "0.18.0", + "@lexical/dragon": "0.18.0", + "@lexical/hashtag": "0.18.0", + "@lexical/history": "0.18.0", + "@lexical/link": "0.18.0", + "@lexical/list": "0.18.0", + "@lexical/mark": "0.18.0", + "@lexical/markdown": "0.18.0", + "@lexical/overflow": "0.18.0", + "@lexical/plain-text": "0.18.0", + "@lexical/rich-text": "0.18.0", + "@lexical/selection": "0.18.0", + "@lexical/table": "0.18.0", + "@lexical/text": "0.18.0", + "@lexical/utils": "0.18.0", + "@lexical/yjs": "0.18.0", + "lexical": "0.18.0", "react-error-boundary": "^3.1.4" }, "peerDependencies": { diff --git a/packages/lexical-react/src/__tests__/unit/LexicalCollaborationPlugin.test.tsx b/packages/lexical-react/src/__tests__/unit/LexicalCollaborationPlugin.test.tsx index 11c02ab3bd2..40a292028b6 100644 --- a/packages/lexical-react/src/__tests__/unit/LexicalCollaborationPlugin.test.tsx +++ b/packages/lexical-react/src/__tests__/unit/LexicalCollaborationPlugin.test.tsx @@ -53,6 +53,7 @@ describe(`LexicalCollaborationPlugin`, () => { off: () => {}, on: () => {}, setLocalState: () => {}, + setLocalStateField: () => {}, }, connect: () => {}, disconnect: () => {}, diff --git a/packages/lexical-react/src/__tests__/unit/utils.tsx b/packages/lexical-react/src/__tests__/unit/utils.tsx index 36b591e1988..66dbfa75084 100644 --- a/packages/lexical-react/src/__tests__/unit/utils.tsx +++ b/packages/lexical-react/src/__tests__/unit/utils.tsx @@ -82,6 +82,7 @@ export class Client implements Provider { off(): void; on(): void; setLocalState: (state: UserState) => void; + setLocalStateField: (field: string, value: unknown) => void; }; constructor(id: Client['_id'], connection: Client['_connection']) { @@ -105,6 +106,9 @@ export class Client implements Provider { setLocalState: (state) => { this._awarenessState = state; }, + setLocalStateField: (field: string, value: unknown) => { + // TODO + }, }; } diff --git a/packages/lexical-rich-text/package.json b/packages/lexical-rich-text/package.json index 39175860776..0bd956b2dff 100644 --- a/packages/lexical-rich-text/package.json +++ b/packages/lexical-rich-text/package.json @@ -7,7 +7,7 @@ "rich-text" ], "license": "MIT", - "version": "0.17.1", + "version": "0.18.0", "main": "LexicalRichText.js", "types": "index.d.ts", "repository": { @@ -35,9 +35,9 @@ } }, "dependencies": { - "@lexical/clipboard": "0.17.1", - "@lexical/selection": "0.17.1", - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/clipboard": "0.18.0", + "@lexical/selection": "0.18.0", + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" } } diff --git a/packages/lexical-selection/package.json b/packages/lexical-selection/package.json index 22a8f87cdfc..a08d821efa5 100644 --- a/packages/lexical-selection/package.json +++ b/packages/lexical-selection/package.json @@ -9,7 +9,7 @@ "selection" ], "license": "MIT", - "version": "0.17.1", + "version": "0.18.0", "main": "LexicalSelection.js", "types": "index.d.ts", "repository": { @@ -37,6 +37,6 @@ } }, "dependencies": { - "lexical": "0.17.1" + "lexical": "0.18.0" } } diff --git a/packages/lexical-table/package.json b/packages/lexical-table/package.json index fb990dbe15c..3e64fd93dbb 100644 --- a/packages/lexical-table/package.json +++ b/packages/lexical-table/package.json @@ -8,13 +8,13 @@ "table" ], "license": "MIT", - "version": "0.17.1", + "version": "0.18.0", "main": "LexicalTable.js", "types": "index.d.ts", "dependencies": { - "@lexical/clipboard": "0.17.1", - "@lexical/utils": "0.17.1", - "lexical": "0.17.1" + "@lexical/clipboard": "0.18.0", + "@lexical/utils": "0.18.0", + "lexical": "0.18.0" }, "repository": { "type": "git", diff --git a/packages/lexical-table/src/LexicalTableSelection.ts b/packages/lexical-table/src/LexicalTableSelection.ts index acb80d2a20d..03c8543cb73 100644 --- a/packages/lexical-table/src/LexicalTableSelection.ts +++ b/packages/lexical-table/src/LexicalTableSelection.ts @@ -11,12 +11,15 @@ import { $createPoint, $getNodeByKey, $isElementNode, + $isParagraphNode, $normalizeSelection__EXPERIMENTAL, BaseSelection, isCurrentlyReadOnlyMode, LexicalNode, NodeKey, PointType, + TEXT_TYPE_TO_FORMAT, + TextFormatType, } from 'lexical'; import invariant from 'shared/invariant'; @@ -116,6 +119,28 @@ export class TableSelection implements BaseSelection { // Do nothing? } + /** + * Returns whether the provided TextFormatType is present on the Selection. + * This will be true if any paragraph in table cells has the specified format. + * + * @param type the TextFormatType to check for. + * @returns true if the provided format is currently toggled on on the Selection, false otherwise. + */ + hasFormat(type: TextFormatType): boolean { + let format = 0; + + const cellNodes = this.getNodes().filter($isTableCellNode); + cellNodes.forEach((cellNode: TableCellNode) => { + const paragraph = cellNode.getFirstChild(); + if ($isParagraphNode(paragraph)) { + format |= paragraph.getTextFormat(); + } + }); + + const formatFlag = TEXT_TYPE_TO_FORMAT[type]; + return (format & formatFlag) !== 0; + } + insertNodes(nodes: Array) { const focusNode = this.focus.getNode(); invariant( diff --git a/packages/lexical-table/src/LexicalTableUtils.ts b/packages/lexical-table/src/LexicalTableUtils.ts index c674c4a8b88..7a4aa7534ea 100644 --- a/packages/lexical-table/src/LexicalTableUtils.ts +++ b/packages/lexical-table/src/LexicalTableUtils.ts @@ -510,8 +510,9 @@ export function $deleteTableRow__EXPERIMENTAL(): void { $isRangeSelection(selection) || $isTableSelection(selection), 'Expected a RangeSelection or TableSelection', ); - const anchor = selection.anchor.getNode(); - const focus = selection.focus.getNode(); + const [anchor, focus] = selection.isBackward() + ? [selection.focus.getNode(), selection.anchor.getNode()] + : [selection.anchor.getNode(), selection.focus.getNode()]; const [anchorCell, , grid] = $getNodeTriplet(anchor); const [focusCell] = $getNodeTriplet(focus); const [gridMap, anchorCellMap, focusCellMap] = $computeTableMap( diff --git a/packages/lexical-text/package.json b/packages/lexical-text/package.json index dbf68a9dba5..9def319d3ed 100644 --- a/packages/lexical-text/package.json +++ b/packages/lexical-text/package.json @@ -9,7 +9,7 @@ "text" ], "license": "MIT", - "version": "0.17.1", + "version": "0.18.0", "main": "LexicalText.js", "types": "index.d.ts", "repository": { @@ -37,6 +37,6 @@ } }, "dependencies": { - "lexical": "0.17.1" + "lexical": "0.18.0" } } diff --git a/packages/lexical-utils/flow/LexicalUtils.js.flow b/packages/lexical-utils/flow/LexicalUtils.js.flow index 51e0216029d..5bdb9c29815 100644 --- a/packages/lexical-utils/flow/LexicalUtils.js.flow +++ b/packages/lexical-utils/flow/LexicalUtils.js.flow @@ -12,10 +12,6 @@ import type { LexicalNode, ElementNode, } from 'lexical'; -export type DFSNode = $ReadOnly<{ - depth: number, - node: LexicalNode, -}>; declare export function addClassNamesToElement( element: HTMLElement, ...classNames: Array @@ -32,11 +28,26 @@ declare export function mediaFileReader( files: Array, acceptableMimeTypes: Array, ): Promise>>; +export type DFSNode = $ReadOnly<{ + depth: number, + node: LexicalNode, +}>; declare export function $dfs( - startingNode?: LexicalNode, - endingNode?: LexicalNode, + startNode?: LexicalNode, + endNode?: LexicalNode, ): Array; -declare function $getDepth(node: LexicalNode): number; +type DFSIterator = { + next: () => IteratorResult; + @@iterator: () => DFSIterator; +}; +declare export function $dfsIterator( + startNode?: LexicalNode, + endNode?: LexicalNode, +): DFSIterator; +declare export function $getNextSiblingOrParentSibling( + node: LexicalNode, +): null | [LexicalNode, number]; +declare export function $getDepth(node: LexicalNode): number; declare export function $getNearestNodeOfType( node: LexicalNode, klass: Class, diff --git a/packages/lexical-utils/package.json b/packages/lexical-utils/package.json index 0ac4aec507c..a228ad0b27b 100644 --- a/packages/lexical-utils/package.json +++ b/packages/lexical-utils/package.json @@ -8,14 +8,14 @@ "utils" ], "license": "MIT", - "version": "0.17.1", + "version": "0.18.0", "main": "LexicalUtils.js", "types": "index.d.ts", "dependencies": { - "@lexical/list": "0.17.1", - "@lexical/selection": "0.17.1", - "@lexical/table": "0.17.1", - "lexical": "0.17.1" + "@lexical/list": "0.18.0", + "@lexical/selection": "0.18.0", + "@lexical/table": "0.18.0", + "lexical": "0.18.0" }, "repository": { "type": "git", diff --git a/packages/lexical-utils/src/__tests__/unit/LexicalNodeHelpers.test.ts b/packages/lexical-utils/src/__tests__/unit/LexicalNodeHelpers.test.ts index 82d2dddf88d..40fab2f26bc 100644 --- a/packages/lexical-utils/src/__tests__/unit/LexicalNodeHelpers.test.ts +++ b/packages/lexical-utils/src/__tests__/unit/LexicalNodeHelpers.test.ts @@ -21,7 +21,7 @@ import { invariant, } from 'lexical/src/__tests__/utils'; -import {$dfs} from '../..'; +import {$dfs, $getNextSiblingOrParentSibling} from '../..'; describe('LexicalNodeHelpers tests', () => { initializeUnitTest((testEnv) => { @@ -232,5 +232,32 @@ describe('LexicalNodeHelpers tests', () => { ]); }); }); + + test('$getNextSiblingOrParentSibling', async () => { + const editor: LexicalEditor = testEnv.editor; + + await editor.update(() => { + const root = $getRoot(); + const paragraph = $createParagraphNode(); + const paragraph2 = $createParagraphNode(); + const text1 = $createTextNode('text1'); + const text2 = $createTextNode('text2').toggleUnmergeable(); + paragraph.append(text1, text2); + root.append(paragraph, paragraph2); + + // Sibling + expect($getNextSiblingOrParentSibling(paragraph)).toEqual([ + paragraph2, + 0, + ]); + expect($getNextSiblingOrParentSibling(text1)).toEqual([text2, 0]); + + // Parent + expect($getNextSiblingOrParentSibling(text2)).toEqual([paragraph2, -1]); + + // Null (end of the tree) + expect($getNextSiblingOrParentSibling(paragraph2)).toBe(null); + }); + }); }); }); diff --git a/packages/lexical-utils/src/index.ts b/packages/lexical-utils/src/index.ts index a8f7047bdfa..f9dad4ea5fc 100644 --- a/packages/lexical-utils/src/index.ts +++ b/packages/lexical-utils/src/index.ts @@ -63,11 +63,6 @@ export const IS_FIREFOX: boolean = IS_FIREFOX_; export const IS_IOS: boolean = IS_IOS_; export const IS_SAFARI: boolean = IS_SAFARI_; -export type DFSNode = Readonly<{ - depth: number; - node: LexicalNode; -}>; - /** * Takes an HTML element and adds the classNames passed within an array, * ignoring any non-string types. A space can be used to add multiple classes @@ -166,59 +161,129 @@ export function mediaFileReader( }); } +export type DFSNode = Readonly<{ + depth: number; + node: LexicalNode; +}>; + /** * "Depth-First Search" starts at the root/top node of a tree and goes as far as it can down a branch end * before backtracking and finding a new path. Consider solving a maze by hugging either wall, moving down a * branch until you hit a dead-end (leaf) and backtracking to find the nearest branching path and repeat. * It will then return all the nodes found in the search in an array of objects. - * @param startingNode - The node to start the search, if ommitted, it will start at the root node. - * @param endingNode - The node to end the search, if ommitted, it will find all descendants of the startingNode. + * @param startNode - The node to start the search, if omitted, it will start at the root node. + * @param endNode - The node to end the search, if omitted, it will find all descendants of the startingNode. * @returns An array of objects of all the nodes found by the search, including their depth into the tree. - * \\{depth: number, node: LexicalNode\\} It will always return at least 1 node (the ending node) so long as it exists + * \\{depth: number, node: LexicalNode\\} It will always return at least 1 node (the start node). */ export function $dfs( - startingNode?: LexicalNode, - endingNode?: LexicalNode, + startNode?: LexicalNode, + endNode?: LexicalNode, ): Array { - const nodes = []; - const start = (startingNode || $getRoot()).getLatest(); - const end = - endingNode || - ($isElementNode(start) ? start.getLastDescendant() || start : start); - let node: LexicalNode | null = start; - let depth = $getDepth(node); - - while (node !== null && !node.is(end)) { - nodes.push({depth, node}); - - if ($isElementNode(node) && node.getChildrenSize() > 0) { - node = node.getFirstChild(); - depth++; - } else { - // Find immediate sibling or nearest parent sibling - let sibling = null; + return Array.from($dfsIterator(startNode, endNode)); +} + +type DFSIterator = { + next: () => IteratorResult; + [Symbol.iterator]: () => DFSIterator; +}; - while (sibling === null && node !== null) { - sibling = node.getNextSibling(); +const iteratorDone: Readonly<{done: true; value: void}> = { + done: true, + value: undefined, +}; +const iteratorNotDone: (value: T) => Readonly<{done: false; value: T}> = ( + value: T, +) => ({done: false, value}); - if (sibling === null) { - node = node.getParent(); - depth--; - } else { - node = sibling; +/** + * $dfs iterator. Tree traversal is done on the fly as new values are requested with O(1) memory. + * @param startNode - The node to start the search, if omitted, it will start at the root node. + * @param endNode - The node to end the search, if omitted, it will find all descendants of the startingNode. + * @returns An iterator, each yielded value is a DFSNode. It will always return at least 1 node (the start node). + */ +export function $dfsIterator( + startNode?: LexicalNode, + endNode?: LexicalNode, +): DFSIterator { + const start = (startNode || $getRoot()).getLatest(); + const startDepth = $getDepth(start); + const end = endNode; + let node: null | LexicalNode = start; + let depth = startDepth; + let isFirstNext = true; + + const iterator: DFSIterator = { + next(): IteratorResult { + if (node === null) { + return iteratorDone; + } + if (isFirstNext) { + isFirstNext = false; + return iteratorNotDone({depth, node}); + } + if (node === end) { + return iteratorDone; + } + + if ($isElementNode(node) && node.getChildrenSize() > 0) { + node = node.getFirstChild(); + depth++; + } else { + let depthDiff; + [node, depthDiff] = $getNextSiblingOrParentSibling(node) || [null, 0]; + depth += depthDiff; + if (end == null && depth <= startDepth) { + node = null; } } + + if (node === null) { + return iteratorDone; + } + return iteratorNotDone({depth, node}); + }, + [Symbol.iterator](): DFSIterator { + return iterator; + }, + }; + return iterator; +} + +/** + * Returns the Node sibling when this exists, otherwise the closest parent sibling. For example + * R -> P -> T1, T2 + * -> P2 + * returns T2 for node T1, P2 for node T2, and null for node P2. + * @param node LexicalNode. + * @returns An array (tuple) containing the found Lexical node and the depth difference, or null, if this node doesn't exist. + */ +export function $getNextSiblingOrParentSibling( + node: LexicalNode, +): null | [LexicalNode, number] { + let node_: null | LexicalNode = node; + // Find immediate sibling or nearest parent sibling + let sibling = null; + let depthDiff = 0; + + while (sibling === null && node_ !== null) { + sibling = node_.getNextSibling(); + + if (sibling === null) { + node_ = node_.getParent(); + depthDiff--; + } else { + node_ = sibling; } } - if (node !== null && node.is(end)) { - nodes.push({depth, node}); + if (node_ === null) { + return null; } - - return nodes; + return [node_, depthDiff]; } -function $getDepth(node: LexicalNode): number { +export function $getDepth(node: LexicalNode): number { let innerNode: LexicalNode | null = node; let depth = 0; diff --git a/packages/lexical-website/package.json b/packages/lexical-website/package.json index daa79715a9b..002e91eebd1 100644 --- a/packages/lexical-website/package.json +++ b/packages/lexical-website/package.json @@ -1,6 +1,6 @@ { "name": "@lexical/website", - "version": "0.17.1", + "version": "0.18.0", "private": true, "scripts": { "docusaurus": "docusaurus", diff --git a/packages/lexical-yjs/package.json b/packages/lexical-yjs/package.json index 4b861b7c8fd..71f38a78c1c 100644 --- a/packages/lexical-yjs/package.json +++ b/packages/lexical-yjs/package.json @@ -11,13 +11,13 @@ "crdt" ], "license": "MIT", - "version": "0.17.1", + "version": "0.18.0", "main": "LexicalYjs.js", "types": "index.d.ts", "dependencies": { - "@lexical/offset": "0.17.1", - "@lexical/selection": "0.17.1", - "lexical": "0.17.1" + "@lexical/offset": "0.18.0", + "@lexical/selection": "0.18.0", + "lexical": "0.18.0" }, "peerDependencies": { "yjs": ">=13.5.22" diff --git a/packages/lexical-yjs/src/index.ts b/packages/lexical-yjs/src/index.ts index 248e344268b..c06a6f06cb6 100644 --- a/packages/lexical-yjs/src/index.ts +++ b/packages/lexical-yjs/src/index.ts @@ -32,6 +32,7 @@ export type ProviderAwareness = { off: (type: 'update', cb: () => void) => void; on: (type: 'update', cb: () => void) => void; setLocalState: (arg0: UserState) => void; + setLocalStateField: (field: string, value: unknown) => void; }; declare interface Provider { awareness: ProviderAwareness; diff --git a/packages/lexical/package.json b/packages/lexical/package.json index 7096a3b74ae..198583a9818 100644 --- a/packages/lexical/package.json +++ b/packages/lexical/package.json @@ -9,7 +9,7 @@ "rich-text" ], "license": "MIT", - "version": "0.17.1", + "version": "0.18.0", "main": "Lexical.js", "types": "index.d.ts", "repository": { diff --git a/packages/lexical/src/LexicalSelection.ts b/packages/lexical/src/LexicalSelection.ts index 3a4e9ac0456..423a11cdf98 100644 --- a/packages/lexical/src/LexicalSelection.ts +++ b/packages/lexical/src/LexicalSelection.ts @@ -1194,12 +1194,21 @@ export class RangeSelection implements BaseSelection { selectedTextNodes.push(selectedNode); } } + const applyFormatToParagraphs = (alignWith: number | null) => { + selectedNodes.forEach((node) => { + if ($isParagraphNode(node)) { + const newFormat = node.getFormatFlags(formatType, alignWith); + node.setTextFormat(newFormat); + } + }); + }; const selectedTextNodesLength = selectedTextNodes.length; if (selectedTextNodesLength === 0) { this.toggleFormat(formatType); // When changing format, we should stop composition $setCompositionKey(null); + applyFormatToParagraphs(alignWithFormat); return; } @@ -1231,12 +1240,7 @@ export class RangeSelection implements BaseSelection { formatType, alignWithFormat, ); - selectedNodes.forEach((node) => { - if ($isParagraphNode(node)) { - const newFormat = node.getFormatFlags(formatType, firstNextFormat); - node.setTextFormat(newFormat); - } - }); + applyFormatToParagraphs(firstNextFormat); const lastIndex = selectedTextNodesLength - 1; let lastNode = selectedTextNodes[lastIndex]; diff --git a/packages/shared/package.json b/packages/shared/package.json index 8fe88be6fb0..951ee6f8f9e 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -8,9 +8,9 @@ "rich-text" ], "license": "MIT", - "version": "0.17.1", + "version": "0.18.0", "dependencies": { - "lexical": "0.17.1" + "lexical": "0.18.0" }, "repository": { "type": "git", diff --git a/scripts/__tests__/integration/fixtures/lexical-esm-astro-react/package.json b/scripts/__tests__/integration/fixtures/lexical-esm-astro-react/package.json index c278cecc2f4..dddf28e1d62 100644 --- a/scripts/__tests__/integration/fixtures/lexical-esm-astro-react/package.json +++ b/scripts/__tests__/integration/fixtures/lexical-esm-astro-react/package.json @@ -1,7 +1,7 @@ { "name": "lexical-esm-astro-react", "type": "module", - "version": "0.17.1", + "version": "0.18.0", "scripts": { "dev": "astro dev", "start": "astro dev", @@ -13,12 +13,12 @@ "dependencies": { "@astrojs/check": "^0.9.3", "@astrojs/react": "^3.1.0", - "@lexical/react": "0.17.1", - "@lexical/utils": "0.17.1", + "@lexical/react": "0.18.0", + "@lexical/utils": "0.18.0", "@types/react": "^18.2.66", "@types/react-dom": "^18.2.22", "astro": "^4.5.4", - "lexical": "0.17.1", + "lexical": "0.18.0", "react": "^18.2.0", "react-dom": "^18.2.0", "typescript": "^5.4.2" @@ -26,5 +26,6 @@ "devDependencies": { "@playwright/test": "^1.43.1" }, - "sideEffects": false + "sideEffects": false, + "exports": {} } diff --git a/scripts/__tests__/integration/fixtures/lexical-esm-nextjs/package.json b/scripts/__tests__/integration/fixtures/lexical-esm-nextjs/package.json index b419263b0c5..94e51d28b20 100644 --- a/scripts/__tests__/integration/fixtures/lexical-esm-nextjs/package.json +++ b/scripts/__tests__/integration/fixtures/lexical-esm-nextjs/package.json @@ -1,6 +1,6 @@ { "name": "lexical-esm-nextjs", - "version": "0.17.1", + "version": "0.18.0", "private": true, "scripts": { "dev": "next dev", @@ -9,9 +9,9 @@ "test": "playwright test" }, "dependencies": { - "@lexical/plain-text": "0.17.1", - "@lexical/react": "0.17.1", - "lexical": "0.17.1", + "@lexical/plain-text": "0.18.0", + "@lexical/react": "0.18.0", + "lexical": "0.18.0", "next": "^14.2.1", "react": "^18", "react-dom": "^18" diff --git a/scripts/__tests__/integration/fixtures/lexical-esm-sveltekit-vanilla-js/package.json b/scripts/__tests__/integration/fixtures/lexical-esm-sveltekit-vanilla-js/package.json index a71328e797f..db051a94a3e 100644 --- a/scripts/__tests__/integration/fixtures/lexical-esm-sveltekit-vanilla-js/package.json +++ b/scripts/__tests__/integration/fixtures/lexical-esm-sveltekit-vanilla-js/package.json @@ -1,6 +1,6 @@ { "name": "lexical-sveltekit-vanilla-js", - "version": "0.17.1", + "version": "0.18.0", "private": true, "scripts": { "dev": "vite dev", @@ -9,17 +9,17 @@ "test": "playwright test" }, "devDependencies": { - "@lexical/dragon": "0.17.1", - "@lexical/history": "0.17.1", - "@lexical/rich-text": "0.17.1", - "@lexical/utils": "0.17.1", + "@lexical/dragon": "0.18.0", + "@lexical/history": "0.18.0", + "@lexical/rich-text": "0.18.0", + "@lexical/utils": "0.18.0", "@playwright/test": "^1.28.1", "@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/adapter-node": "^5.0.1", "@sveltejs/adapter-static": "^3.0.1", "@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0", - "lexical": "0.17.1", + "lexical": "0.18.0", "prettier": "^3.1.1", "prettier-plugin-svelte": "^3.1.2", "svelte": "^4.2.19", diff --git a/scripts/error-codes/codes.json b/scripts/error-codes/codes.json index 0d5ae282bf6..92a650456c6 100644 --- a/scripts/error-codes/codes.json +++ b/scripts/error-codes/codes.json @@ -198,5 +198,16 @@ "196": "Unable to find an active editor. This method can only be used synchronously during the callback of editor.update() or editor.read().%s", "197": "$cloneWithProperties: %s.clone(node) (with type '%s') overrided afterCloneFrom but did not call super.afterCloneFrom(prevNode)", "198": "Attempted to remove event handlers from a node that does not belong to this build of Lexical", - "199": "Indent value must be non-negative." + "199": "Indent value must be non-negative.", + "200": "$applyNodeReplacement node %s with type %s must be registered to the editor. You can do this by passing the node class via the \"nodes\" array in the editor config.", + "201": "$applyNodeReplacement failed. Expected replacement node to be an instance of %s with type %s but returned %s with type %s from original node %s with type %s", + "202": "$applyNodeReplacement failed. Ensure replacement node %s with type %s is a subclass of the original node %s with type %s.", + "203": "$applyNodeReplacement failed. Ensure that the key argument is *not* used in your replace function (from node %s with type %s to node %s with type %s), Node keys must never be re-used except by the static clone method.", + "204": "node is not a OverflowNode", + "205": "tableElement already has an attached TableObserver", + "206": "Expected TableNode childAtIndex(%s) to be RowNode", + "207": "Anchor not found in Table", + "208": "Focus not found in Table", + "209": "Expected TableNode children to be TableRowNode", + "210": "Expected TableRowNode to have a parent TableNode" }