From acb0b91578f92a62e6cae493aa0c1ef98687dccb Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Wed, 1 May 2024 08:30:56 +0100 Subject: [PATCH 01/63] Commit yarn.lock --- yarn.lock | 334 +++++++++++++++++++++++++++--------------------------- 1 file changed, 167 insertions(+), 167 deletions(-) diff --git a/yarn.lock b/yarn.lock index 9b31db3754..7c7ad62173 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5475,13 +5475,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-alignment@npm:31.0.0, @udecode/plate-alignment@workspace:^, @udecode/plate-alignment@workspace:packages/alignment": +"@udecode/plate-alignment@npm:32.0.0, @udecode/plate-alignment@workspace:^, @udecode/plate-alignment@workspace:packages/alignment": version: 0.0.0-use.local resolution: "@udecode/plate-alignment@workspace:packages/alignment" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5491,14 +5491,14 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-autoformat@npm:31.0.0, @udecode/plate-autoformat@workspace:^, @udecode/plate-autoformat@workspace:packages/autoformat": +"@udecode/plate-autoformat@npm:32.0.0, @udecode/plate-autoformat@workspace:^, @udecode/plate-autoformat@workspace:packages/autoformat": version: 0.0.0-use.local resolution: "@udecode/plate-autoformat@workspace:packages/autoformat" dependencies: "@udecode/plate-common": "workspace:^" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5508,17 +5508,17 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-basic-elements@npm:31.4.3, @udecode/plate-basic-elements@workspace:^, @udecode/plate-basic-elements@workspace:packages/basic-elements": +"@udecode/plate-basic-elements@npm:32.0.0, @udecode/plate-basic-elements@workspace:^, @udecode/plate-basic-elements@workspace:packages/basic-elements": version: 0.0.0-use.local resolution: "@udecode/plate-basic-elements@workspace:packages/basic-elements" dependencies: - "@udecode/plate-block-quote": "npm:31.4.3" - "@udecode/plate-code-block": "npm:31.3.4" + "@udecode/plate-block-quote": "npm:32.0.0" + "@udecode/plate-code-block": "npm:32.0.0" "@udecode/plate-common": "workspace:^" - "@udecode/plate-heading": "npm:31.0.0" - "@udecode/plate-paragraph": "npm:31.0.0" + "@udecode/plate-heading": "npm:32.0.0" + "@udecode/plate-paragraph": "npm:32.0.0" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5528,13 +5528,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-basic-marks@npm:31.0.0, @udecode/plate-basic-marks@workspace:^, @udecode/plate-basic-marks@workspace:packages/basic-marks": +"@udecode/plate-basic-marks@npm:32.0.0, @udecode/plate-basic-marks@workspace:^, @udecode/plate-basic-marks@workspace:packages/basic-marks": version: 0.0.0-use.local resolution: "@udecode/plate-basic-marks@workspace:packages/basic-marks" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5544,13 +5544,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-block-quote@npm:31.4.3, @udecode/plate-block-quote@workspace:^, @udecode/plate-block-quote@workspace:packages/block-quote": +"@udecode/plate-block-quote@npm:32.0.0, @udecode/plate-block-quote@workspace:^, @udecode/plate-block-quote@workspace:packages/block-quote": version: 0.0.0-use.local resolution: "@udecode/plate-block-quote@workspace:packages/block-quote" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5560,13 +5560,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-break@npm:31.0.0, @udecode/plate-break@workspace:^, @udecode/plate-break@workspace:packages/break": +"@udecode/plate-break@npm:32.0.0, @udecode/plate-break@workspace:^, @udecode/plate-break@workspace:packages/break": version: 0.0.0-use.local resolution: "@udecode/plate-break@workspace:packages/break" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5583,7 +5583,7 @@ __metadata: "@udecode/plate-common": "workspace:^" react-textarea-autosize: "npm:^8.5.3" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5602,7 +5602,7 @@ __metadata: delay: "npm:5.0.0" p-defer: "npm:^3.0.0" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5612,14 +5612,14 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-code-block@npm:31.3.4, @udecode/plate-code-block@workspace:^, @udecode/plate-code-block@workspace:packages/code-block": +"@udecode/plate-code-block@npm:32.0.0, @udecode/plate-code-block@workspace:^, @udecode/plate-code-block@workspace:packages/code-block": version: 0.0.0-use.local resolution: "@udecode/plate-code-block@workspace:packages/code-block" dependencies: "@udecode/plate-common": "workspace:^" prismjs: "npm:^1.29.0" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5629,14 +5629,14 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-combobox@npm:31.0.0, @udecode/plate-combobox@workspace:^, @udecode/plate-combobox@workspace:packages/combobox": +"@udecode/plate-combobox@npm:32.0.0, @udecode/plate-combobox@workspace:^, @udecode/plate-combobox@workspace:packages/combobox": version: 0.0.0-use.local resolution: "@udecode/plate-combobox@workspace:packages/combobox" dependencies: "@udecode/plate-common": "workspace:^" downshift: "npm:^6.1.12" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5646,14 +5646,14 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-comments@npm:31.0.0, @udecode/plate-comments@workspace:^, @udecode/plate-comments@workspace:packages/comments": +"@udecode/plate-comments@npm:32.0.0, @udecode/plate-comments@workspace:^, @udecode/plate-comments@workspace:packages/comments": version: 0.0.0-use.local resolution: "@udecode/plate-comments@workspace:packages/comments" dependencies: "@udecode/plate-common": "workspace:^" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5663,12 +5663,12 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-common@npm:31.3.2, @udecode/plate-common@workspace:^, @udecode/plate-common@workspace:packages/common": +"@udecode/plate-common@npm:32.0.0, @udecode/plate-common@workspace:^, @udecode/plate-common@workspace:packages/common": version: 0.0.0-use.local resolution: "@udecode/plate-common@workspace:packages/common" dependencies: - "@udecode/plate-core": "npm:31.3.2" - "@udecode/plate-utils": "npm:31.3.2" + "@udecode/plate-core": "npm:32.0.0" + "@udecode/plate-utils": "npm:32.0.0" "@udecode/react-utils": "npm:31.0.0" "@udecode/slate": "npm:31.0.0" "@udecode/slate-react": "npm:31.0.0" @@ -5684,7 +5684,7 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-core@npm:31.3.2, @udecode/plate-core@workspace:^, @udecode/plate-core@workspace:packages/core": +"@udecode/plate-core@npm:32.0.0, @udecode/plate-core@workspace:^, @udecode/plate-core@workspace:packages/core": version: 0.0.0-use.local resolution: "@udecode/plate-core@workspace:packages/core" dependencies: @@ -5720,7 +5720,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5730,14 +5730,14 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-diff@npm:31.0.0, @udecode/plate-diff@workspace:^, @udecode/plate-diff@workspace:packages/diff": +"@udecode/plate-diff@npm:32.0.0, @udecode/plate-diff@workspace:^, @udecode/plate-diff@workspace:packages/diff": version: 0.0.0-use.local resolution: "@udecode/plate-diff@workspace:packages/diff" dependencies: "@udecode/plate-common": "workspace:^" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5755,7 +5755,7 @@ __metadata: lodash: "npm:^4.17.21" raf: "npm:^3.4.1" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dnd: ">=14.0.0" react-dnd-html5-backend: ">=14.0.0" @@ -5772,10 +5772,10 @@ __metadata: resolution: "@udecode/plate-emoji@workspace:packages/emoji" dependencies: "@emoji-mart/data": "npm:^1.1.2" - "@udecode/plate-combobox": "npm:31.0.0" + "@udecode/plate-combobox": "npm:32.0.0" "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5792,7 +5792,7 @@ __metadata: "@excalidraw/excalidraw": "npm:0.16.4" "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5802,13 +5802,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-find-replace@npm:31.0.0, @udecode/plate-find-replace@workspace:^, @udecode/plate-find-replace@workspace:packages/find-replace": +"@udecode/plate-find-replace@npm:32.0.0, @udecode/plate-find-replace@workspace:^, @udecode/plate-find-replace@workspace:packages/find-replace": version: 0.0.0-use.local resolution: "@udecode/plate-find-replace@workspace:packages/find-replace" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5818,7 +5818,7 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-floating@npm:31.0.0, @udecode/plate-floating@workspace:^, @udecode/plate-floating@workspace:packages/floating": +"@udecode/plate-floating@npm:32.0.0, @udecode/plate-floating@workspace:^, @udecode/plate-floating@workspace:packages/floating": version: 0.0.0-use.local resolution: "@udecode/plate-floating@workspace:packages/floating" dependencies: @@ -5826,7 +5826,7 @@ __metadata: "@floating-ui/react": "npm:^0.22.3" "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5836,14 +5836,14 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-font@npm:31.0.0, @udecode/plate-font@workspace:^, @udecode/plate-font@workspace:packages/font": +"@udecode/plate-font@npm:32.0.0, @udecode/plate-font@workspace:^, @udecode/plate-font@workspace:packages/font": version: 0.0.0-use.local resolution: "@udecode/plate-font@workspace:packages/font" dependencies: "@udecode/plate-common": "workspace:^" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5853,13 +5853,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-heading@npm:31.0.0, @udecode/plate-heading@workspace:^, @udecode/plate-heading@workspace:packages/heading": +"@udecode/plate-heading@npm:32.0.0, @udecode/plate-heading@workspace:^, @udecode/plate-heading@workspace:packages/heading": version: 0.0.0-use.local resolution: "@udecode/plate-heading@workspace:packages/heading" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5869,13 +5869,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-highlight@npm:31.0.0, @udecode/plate-highlight@workspace:^, @udecode/plate-highlight@workspace:packages/highlight": +"@udecode/plate-highlight@npm:32.0.0, @udecode/plate-highlight@workspace:^, @udecode/plate-highlight@workspace:packages/highlight": version: 0.0.0-use.local resolution: "@udecode/plate-highlight@workspace:packages/highlight" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5885,13 +5885,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-horizontal-rule@npm:31.0.0, @udecode/plate-horizontal-rule@workspace:^, @udecode/plate-horizontal-rule@workspace:packages/horizontal-rule": +"@udecode/plate-horizontal-rule@npm:32.0.0, @udecode/plate-horizontal-rule@workspace:^, @udecode/plate-horizontal-rule@workspace:packages/horizontal-rule": version: 0.0.0-use.local resolution: "@udecode/plate-horizontal-rule@workspace:packages/horizontal-rule" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5901,16 +5901,16 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-indent-list@npm:31.4.3, @udecode/plate-indent-list@workspace:^, @udecode/plate-indent-list@workspace:packages/indent-list": +"@udecode/plate-indent-list@npm:32.0.0, @udecode/plate-indent-list@workspace:^, @udecode/plate-indent-list@workspace:packages/indent-list": version: 0.0.0-use.local resolution: "@udecode/plate-indent-list@workspace:packages/indent-list" dependencies: "@udecode/plate-common": "workspace:^" - "@udecode/plate-indent": "npm:31.1.0" - "@udecode/plate-list": "npm:31.1.3" + "@udecode/plate-indent": "npm:32.0.0" + "@udecode/plate-list": "npm:32.0.0" clsx: "npm:^1.2.1" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5920,13 +5920,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-indent@npm:31.1.0, @udecode/plate-indent@workspace:^, @udecode/plate-indent@workspace:packages/indent": +"@udecode/plate-indent@npm:32.0.0, @udecode/plate-indent@workspace:^, @udecode/plate-indent@workspace:packages/indent": version: 0.0.0-use.local resolution: "@udecode/plate-indent@workspace:packages/indent" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5943,7 +5943,7 @@ __metadata: "@udecode/plate-common": "workspace:^" juice: "npm:^8.1.0" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5953,13 +5953,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-kbd@npm:31.0.0, @udecode/plate-kbd@workspace:^, @udecode/plate-kbd@workspace:packages/kbd": +"@udecode/plate-kbd@npm:32.0.0, @udecode/plate-kbd@workspace:^, @udecode/plate-kbd@workspace:packages/kbd": version: 0.0.0-use.local resolution: "@udecode/plate-kbd@workspace:packages/kbd" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5975,7 +5975,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5985,13 +5985,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-line-height@npm:31.0.0, @udecode/plate-line-height@workspace:^, @udecode/plate-line-height@workspace:packages/line-height": +"@udecode/plate-line-height@npm:32.0.0, @udecode/plate-line-height@workspace:^, @udecode/plate-line-height@workspace:packages/line-height": version: 0.0.0-use.local resolution: "@udecode/plate-line-height@workspace:packages/line-height" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6001,15 +6001,15 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-link@npm:31.0.0, @udecode/plate-link@workspace:^, @udecode/plate-link@workspace:packages/link": +"@udecode/plate-link@npm:32.0.0, @udecode/plate-link@workspace:^, @udecode/plate-link@workspace:packages/link": version: 0.0.0-use.local resolution: "@udecode/plate-link@workspace:packages/link" dependencies: "@udecode/plate-common": "workspace:^" - "@udecode/plate-floating": "npm:31.0.0" - "@udecode/plate-normalizers": "npm:31.0.0" + "@udecode/plate-floating": "npm:32.0.0" + "@udecode/plate-normalizers": "npm:32.0.0" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6019,15 +6019,15 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-list@npm:31.1.3, @udecode/plate-list@workspace:^, @udecode/plate-list@workspace:packages/list": +"@udecode/plate-list@npm:32.0.0, @udecode/plate-list@workspace:^, @udecode/plate-list@workspace:packages/list": version: 0.0.0-use.local resolution: "@udecode/plate-list@workspace:packages/list" dependencies: "@udecode/plate-common": "workspace:^" - "@udecode/plate-reset-node": "npm:31.0.0" + "@udecode/plate-reset-node": "npm:32.0.0" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6037,14 +6037,14 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-media@npm:31.0.0, @udecode/plate-media@workspace:^, @udecode/plate-media@workspace:packages/media": +"@udecode/plate-media@npm:32.0.0, @udecode/plate-media@workspace:^, @udecode/plate-media@workspace:packages/media": version: 0.0.0-use.local resolution: "@udecode/plate-media@workspace:packages/media" dependencies: "@udecode/plate-common": "workspace:^" js-video-url-parser: "npm:^0.5.1" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6054,14 +6054,14 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-mention@npm:31.3.5, @udecode/plate-mention@workspace:^, @udecode/plate-mention@workspace:packages/mention": +"@udecode/plate-mention@npm:32.0.0, @udecode/plate-mention@workspace:^, @udecode/plate-mention@workspace:packages/mention": version: 0.0.0-use.local resolution: "@udecode/plate-mention@workspace:packages/mention" dependencies: - "@udecode/plate-combobox": "npm:31.0.0" + "@udecode/plate-combobox": "npm:32.0.0" "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6071,14 +6071,14 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-node-id@npm:31.0.0, @udecode/plate-node-id@workspace:^, @udecode/plate-node-id@workspace:packages/node-id": +"@udecode/plate-node-id@npm:32.0.0, @udecode/plate-node-id@workspace:^, @udecode/plate-node-id@workspace:packages/node-id": version: 0.0.0-use.local resolution: "@udecode/plate-node-id@workspace:packages/node-id" dependencies: "@udecode/plate-common": "workspace:^" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6088,14 +6088,14 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-normalizers@npm:31.0.0, @udecode/plate-normalizers@workspace:^, @udecode/plate-normalizers@workspace:packages/normalizers": +"@udecode/plate-normalizers@npm:32.0.0, @udecode/plate-normalizers@workspace:^, @udecode/plate-normalizers@workspace:packages/normalizers": version: 0.0.0-use.local resolution: "@udecode/plate-normalizers@workspace:packages/normalizers" dependencies: "@udecode/plate-common": "workspace:^" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6105,13 +6105,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-paragraph@npm:31.0.0, @udecode/plate-paragraph@workspace:^, @udecode/plate-paragraph@workspace:packages/paragraph": +"@udecode/plate-paragraph@npm:32.0.0, @udecode/plate-paragraph@workspace:^, @udecode/plate-paragraph@workspace:packages/paragraph": version: 0.0.0-use.local resolution: "@udecode/plate-paragraph@workspace:packages/paragraph" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6121,13 +6121,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-reset-node@npm:31.0.0, @udecode/plate-reset-node@workspace:^, @udecode/plate-reset-node@workspace:packages/reset-node": +"@udecode/plate-reset-node@npm:32.0.0, @udecode/plate-reset-node@workspace:^, @udecode/plate-reset-node@workspace:packages/reset-node": version: 0.0.0-use.local resolution: "@udecode/plate-reset-node@workspace:packages/reset-node" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6137,13 +6137,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-resizable@npm:31.0.0, @udecode/plate-resizable@workspace:^, @udecode/plate-resizable@workspace:packages/resizable": +"@udecode/plate-resizable@npm:32.0.0, @udecode/plate-resizable@workspace:^, @udecode/plate-resizable@workspace:packages/resizable": version: 0.0.0-use.local resolution: "@udecode/plate-resizable@workspace:packages/resizable" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6153,13 +6153,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-select@npm:31.0.0, @udecode/plate-select@workspace:^, @udecode/plate-select@workspace:packages/select": +"@udecode/plate-select@npm:32.0.0, @udecode/plate-select@workspace:^, @udecode/plate-select@workspace:packages/select": version: 0.0.0-use.local resolution: "@udecode/plate-select@workspace:packages/select" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6177,7 +6177,7 @@ __metadata: "@viselect/vanilla": "npm:3.2.5" copy-to-clipboard: "npm:^3.3.3" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6187,16 +6187,16 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-serializer-csv@npm:31.4.1, @udecode/plate-serializer-csv@workspace:^, @udecode/plate-serializer-csv@workspace:packages/serializer-csv": +"@udecode/plate-serializer-csv@npm:32.0.0, @udecode/plate-serializer-csv@workspace:^, @udecode/plate-serializer-csv@workspace:packages/serializer-csv": version: 0.0.0-use.local resolution: "@udecode/plate-serializer-csv@workspace:packages/serializer-csv" dependencies: "@types/papaparse": "npm:^5.3.14" "@udecode/plate-common": "workspace:^" - "@udecode/plate-table": "npm:31.4.1" + "@udecode/plate-table": "npm:32.0.0" papaparse: "npm:^5.4.1" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6206,20 +6206,20 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-serializer-docx@npm:31.4.3, @udecode/plate-serializer-docx@workspace:^, @udecode/plate-serializer-docx@workspace:packages/serializer-docx": +"@udecode/plate-serializer-docx@npm:32.0.0, @udecode/plate-serializer-docx@workspace:^, @udecode/plate-serializer-docx@workspace:packages/serializer-docx": version: 0.0.0-use.local resolution: "@udecode/plate-serializer-docx@workspace:packages/serializer-docx" dependencies: "@udecode/plate-common": "workspace:^" - "@udecode/plate-heading": "npm:31.0.0" - "@udecode/plate-indent": "npm:31.1.0" - "@udecode/plate-indent-list": "npm:31.4.3" - "@udecode/plate-media": "npm:31.0.0" - "@udecode/plate-paragraph": "npm:31.0.0" - "@udecode/plate-table": "npm:31.4.1" + "@udecode/plate-heading": "npm:32.0.0" + "@udecode/plate-indent": "npm:32.0.0" + "@udecode/plate-indent-list": "npm:32.0.0" + "@udecode/plate-media": "npm:32.0.0" + "@udecode/plate-paragraph": "npm:32.0.0" + "@udecode/plate-table": "npm:32.0.0" validator: "npm:^13.11.0" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6229,7 +6229,7 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-serializer-html@npm:31.4.4, @udecode/plate-serializer-html@workspace:^, @udecode/plate-serializer-html@workspace:packages/serializer-html": +"@udecode/plate-serializer-html@npm:32.0.0, @udecode/plate-serializer-html@workspace:^, @udecode/plate-serializer-html@workspace:packages/serializer-html": version: 0.0.0-use.local resolution: "@udecode/plate-serializer-html@workspace:packages/serializer-html" dependencies: @@ -6237,7 +6237,7 @@ __metadata: "@udecode/plate-common": "workspace:^" html-entities: "npm:^2.5.2" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6247,24 +6247,24 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-serializer-md@npm:31.4.3, @udecode/plate-serializer-md@workspace:^, @udecode/plate-serializer-md@workspace:packages/serializer-md": +"@udecode/plate-serializer-md@npm:32.0.0, @udecode/plate-serializer-md@workspace:^, @udecode/plate-serializer-md@workspace:packages/serializer-md": version: 0.0.0-use.local resolution: "@udecode/plate-serializer-md@workspace:packages/serializer-md" dependencies: - "@udecode/plate-basic-marks": "npm:31.0.0" - "@udecode/plate-block-quote": "npm:31.4.3" - "@udecode/plate-code-block": "npm:31.3.4" + "@udecode/plate-basic-marks": "npm:32.0.0" + "@udecode/plate-block-quote": "npm:32.0.0" + "@udecode/plate-code-block": "npm:32.0.0" "@udecode/plate-common": "workspace:^" - "@udecode/plate-heading": "npm:31.0.0" - "@udecode/plate-horizontal-rule": "npm:31.0.0" - "@udecode/plate-link": "npm:31.0.0" - "@udecode/plate-list": "npm:31.1.3" - "@udecode/plate-media": "npm:31.0.0" - "@udecode/plate-paragraph": "npm:31.0.0" + "@udecode/plate-heading": "npm:32.0.0" + "@udecode/plate-horizontal-rule": "npm:32.0.0" + "@udecode/plate-link": "npm:32.0.0" + "@udecode/plate-list": "npm:32.0.0" + "@udecode/plate-media": "npm:32.0.0" + "@udecode/plate-paragraph": "npm:32.0.0" remark-parse: "npm:^9.0.0" unified: "npm:^9.2.2" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6278,10 +6278,10 @@ __metadata: version: 0.0.0-use.local resolution: "@udecode/plate-slash-command@workspace:packages/slash-command" dependencies: - "@udecode/plate-combobox": "npm:31.0.0" + "@udecode/plate-combobox": "npm:32.0.0" "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6291,15 +6291,15 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-suggestion@npm:31.0.0, @udecode/plate-suggestion@workspace:^, @udecode/plate-suggestion@workspace:packages/suggestion": +"@udecode/plate-suggestion@npm:32.0.0, @udecode/plate-suggestion@workspace:^, @udecode/plate-suggestion@workspace:packages/suggestion": version: 0.0.0-use.local resolution: "@udecode/plate-suggestion@workspace:packages/suggestion" dependencies: "@udecode/plate-common": "workspace:^" - "@udecode/plate-diff": "npm:31.0.0" + "@udecode/plate-diff": "npm:32.0.0" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6309,14 +6309,14 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-tabbable@npm:31.0.0, @udecode/plate-tabbable@workspace:^, @udecode/plate-tabbable@workspace:packages/tabbable": +"@udecode/plate-tabbable@npm:32.0.0, @udecode/plate-tabbable@workspace:^, @udecode/plate-tabbable@workspace:packages/tabbable": version: 0.0.0-use.local resolution: "@udecode/plate-tabbable@workspace:packages/tabbable" dependencies: "@udecode/plate-common": "workspace:^" tabbable: "npm:^6.2.0" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6326,15 +6326,15 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-table@npm:31.4.1, @udecode/plate-table@workspace:^, @udecode/plate-table@workspace:packages/table": +"@udecode/plate-table@npm:32.0.0, @udecode/plate-table@workspace:^, @udecode/plate-table@workspace:packages/table": version: 0.0.0-use.local resolution: "@udecode/plate-table@workspace:packages/table" dependencies: "@udecode/plate-common": "workspace:^" - "@udecode/plate-resizable": "npm:31.0.0" + "@udecode/plate-resizable": "npm:32.0.0" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6352,16 +6352,16 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-toggle@npm:31.4.0, @udecode/plate-toggle@workspace:^, @udecode/plate-toggle@workspace:packages/toggle": +"@udecode/plate-toggle@npm:32.0.0, @udecode/plate-toggle@workspace:^, @udecode/plate-toggle@workspace:packages/toggle": version: 0.0.0-use.local resolution: "@udecode/plate-toggle@workspace:packages/toggle" dependencies: "@udecode/plate-common": "workspace:^" - "@udecode/plate-indent": "npm:31.1.0" - "@udecode/plate-node-id": "npm:31.0.0" + "@udecode/plate-indent": "npm:32.0.0" + "@udecode/plate-node-id": "npm:32.0.0" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6371,13 +6371,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-trailing-block@npm:31.0.0, @udecode/plate-trailing-block@workspace:^, @udecode/plate-trailing-block@workspace:packages/trailing-block": +"@udecode/plate-trailing-block@npm:32.0.0, @udecode/plate-trailing-block@workspace:^, @udecode/plate-trailing-block@workspace:packages/trailing-block": version: 0.0.0-use.local resolution: "@udecode/plate-trailing-block@workspace:packages/trailing-block" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6418,11 +6418,11 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-utils@npm:31.3.2, @udecode/plate-utils@workspace:^, @udecode/plate-utils@workspace:packages/plate-utils": +"@udecode/plate-utils@npm:32.0.0, @udecode/plate-utils@workspace:^, @udecode/plate-utils@workspace:packages/plate-utils": version: 0.0.0-use.local resolution: "@udecode/plate-utils@workspace:packages/plate-utils" dependencies: - "@udecode/plate-core": "npm:31.3.2" + "@udecode/plate-core": "npm:32.0.0" "@udecode/react-utils": "npm:31.0.0" "@udecode/slate": "npm:31.0.0" "@udecode/slate-react": "npm:31.0.0" @@ -6449,7 +6449,7 @@ __metadata: "@udecode/plate-common": "workspace:^" yjs: "npm:^13.6.14" peerDependencies: - "@udecode/plate-common": ">=31.3.2" + "@udecode/plate-common": ">=32.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6463,46 +6463,46 @@ __metadata: version: 0.0.0-use.local resolution: "@udecode/plate@workspace:packages/plate" dependencies: - "@udecode/plate-alignment": "npm:31.0.0" - "@udecode/plate-autoformat": "npm:31.0.0" - "@udecode/plate-basic-elements": "npm:31.4.3" - "@udecode/plate-basic-marks": "npm:31.0.0" - "@udecode/plate-block-quote": "npm:31.4.3" - "@udecode/plate-break": "npm:31.0.0" - "@udecode/plate-code-block": "npm:31.3.4" - "@udecode/plate-combobox": "npm:31.0.0" - "@udecode/plate-comments": "npm:31.0.0" - "@udecode/plate-common": "npm:31.3.2" - "@udecode/plate-diff": "npm:31.0.0" - "@udecode/plate-find-replace": "npm:31.0.0" - "@udecode/plate-floating": "npm:31.0.0" - "@udecode/plate-font": "npm:31.0.0" - "@udecode/plate-heading": "npm:31.0.0" - "@udecode/plate-highlight": "npm:31.0.0" - "@udecode/plate-horizontal-rule": "npm:31.0.0" - "@udecode/plate-indent": "npm:31.1.0" - "@udecode/plate-indent-list": "npm:31.4.3" - "@udecode/plate-kbd": "npm:31.0.0" - "@udecode/plate-line-height": "npm:31.0.0" - "@udecode/plate-link": "npm:31.0.0" - "@udecode/plate-list": "npm:31.1.3" - "@udecode/plate-media": "npm:31.0.0" - "@udecode/plate-mention": "npm:31.3.5" - "@udecode/plate-node-id": "npm:31.0.0" - "@udecode/plate-normalizers": "npm:31.0.0" - "@udecode/plate-paragraph": "npm:31.0.0" - "@udecode/plate-reset-node": "npm:31.0.0" - "@udecode/plate-resizable": "npm:31.0.0" - "@udecode/plate-select": "npm:31.0.0" - "@udecode/plate-serializer-csv": "npm:31.4.1" - "@udecode/plate-serializer-docx": "npm:31.4.3" - "@udecode/plate-serializer-html": "npm:31.4.4" - "@udecode/plate-serializer-md": "npm:31.4.3" - "@udecode/plate-suggestion": "npm:31.0.0" - "@udecode/plate-tabbable": "npm:31.0.0" - "@udecode/plate-table": "npm:31.4.1" - "@udecode/plate-toggle": "npm:31.4.0" - "@udecode/plate-trailing-block": "npm:31.0.0" + "@udecode/plate-alignment": "npm:32.0.0" + "@udecode/plate-autoformat": "npm:32.0.0" + "@udecode/plate-basic-elements": "npm:32.0.0" + "@udecode/plate-basic-marks": "npm:32.0.0" + "@udecode/plate-block-quote": "npm:32.0.0" + "@udecode/plate-break": "npm:32.0.0" + "@udecode/plate-code-block": "npm:32.0.0" + "@udecode/plate-combobox": "npm:32.0.0" + "@udecode/plate-comments": "npm:32.0.0" + "@udecode/plate-common": "npm:32.0.0" + "@udecode/plate-diff": "npm:32.0.0" + "@udecode/plate-find-replace": "npm:32.0.0" + "@udecode/plate-floating": "npm:32.0.0" + "@udecode/plate-font": "npm:32.0.0" + "@udecode/plate-heading": "npm:32.0.0" + "@udecode/plate-highlight": "npm:32.0.0" + "@udecode/plate-horizontal-rule": "npm:32.0.0" + "@udecode/plate-indent": "npm:32.0.0" + "@udecode/plate-indent-list": "npm:32.0.0" + "@udecode/plate-kbd": "npm:32.0.0" + "@udecode/plate-line-height": "npm:32.0.0" + "@udecode/plate-link": "npm:32.0.0" + "@udecode/plate-list": "npm:32.0.0" + "@udecode/plate-media": "npm:32.0.0" + "@udecode/plate-mention": "npm:32.0.0" + "@udecode/plate-node-id": "npm:32.0.0" + "@udecode/plate-normalizers": "npm:32.0.0" + "@udecode/plate-paragraph": "npm:32.0.0" + "@udecode/plate-reset-node": "npm:32.0.0" + "@udecode/plate-resizable": "npm:32.0.0" + "@udecode/plate-select": "npm:32.0.0" + "@udecode/plate-serializer-csv": "npm:32.0.0" + "@udecode/plate-serializer-docx": "npm:32.0.0" + "@udecode/plate-serializer-html": "npm:32.0.0" + "@udecode/plate-serializer-md": "npm:32.0.0" + "@udecode/plate-suggestion": "npm:32.0.0" + "@udecode/plate-tabbable": "npm:32.0.0" + "@udecode/plate-table": "npm:32.0.0" + "@udecode/plate-toggle": "npm:32.0.0" + "@udecode/plate-trailing-block": "npm:32.0.0" peerDependencies: react: ">=16.8.0" react-dom: ">=16.8.0" From 869f8689f5d913946854fa26c6790857fdc14d16 Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Thu, 25 Apr 2024 16:39:24 +0100 Subject: [PATCH 02/63] Fix: www app uses separate slate-react to packages, breaking useSelected --- apps/www/package.json | 1 - yarn.lock | 1 - 2 files changed, 2 deletions(-) diff --git a/apps/www/package.json b/apps/www/package.json index 48ba29b6a2..8b796567f2 100644 --- a/apps/www/package.json +++ b/apps/www/package.json @@ -135,7 +135,6 @@ "slate": "0.102.0", "slate-history": "0.100.0", "slate-hyperscript": "0.100.0", - "slate-react": "0.102.0", "slate-test-utils": "1.3.2", "sonner": "^1.4.32", "tailwind-merge": "^2.2.2", diff --git a/yarn.lock b/yarn.lock index 7c7ad62173..99dccfa06e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21434,7 +21434,6 @@ __metadata: slate: "npm:0.102.0" slate-history: "npm:0.100.0" slate-hyperscript: "npm:0.100.0" - slate-react: "npm:0.102.0" slate-test-utils: "npm:1.3.2" sonner: "npm:^1.4.32" tailwind-merge: "npm:^2.2.2" From 437738e89aacee40718fbde4ab6d0111f94ef1db Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Wed, 1 May 2024 16:59:44 +0100 Subject: [PATCH 03/63] Prototype combobox input inside slash-input-element.tsx --- apps/www/package.json | 1 + .../default/example/playground-demo.tsx | 3 +- .../default/plate-ui/mention-element.tsx | 4 + .../plate-ui/mention-input-element.tsx | 4 + .../default/plate-ui/slash-input-element.tsx | 313 ++++++++++++++++-- .../slash-command/src/createSlashPlugin.ts | 17 +- .../slash-command/src/withSlashCommand.ts | 139 ++------ yarn.lock | 44 +++ 8 files changed, 377 insertions(+), 148 deletions(-) diff --git a/apps/www/package.json b/apps/www/package.json index 8b796567f2..e5fb76276c 100644 --- a/apps/www/package.json +++ b/apps/www/package.json @@ -17,6 +17,7 @@ "typecheck": "yarn prebuild && tsc --noEmit" }, "dependencies": { + "@ariakit/react": "0.4.6", "@faker-js/faker": "^8.4.1", "@radix-ui/colors": "1.0.1", "@radix-ui/react-accessible-icon": "^1.0.3", diff --git a/apps/www/src/registry/default/example/playground-demo.tsx b/apps/www/src/registry/default/example/playground-demo.tsx index 80a57ff42a..f3b3d6d629 100644 --- a/apps/www/src/registry/default/example/playground-demo.tsx +++ b/apps/www/src/registry/default/example/playground-demo.tsx @@ -130,7 +130,6 @@ import { TodoMarker, } from '@/registry/default/plate-ui/indent-todo-marker-component'; import { MentionCombobox } from '@/registry/default/plate-ui/mention-combobox'; -import { SlashCombobox } from '@/registry/default/plate-ui/slash-combobox'; export const usePlaygroundPlugins = ({ id, @@ -439,7 +438,7 @@ export default function PlaygroundDemo({ id }: { id?: ValueId }) { )} - + {/**/} {isEnabled('cursoroverlay', id) && ( diff --git a/apps/www/src/registry/default/plate-ui/mention-element.tsx b/apps/www/src/registry/default/plate-ui/mention-element.tsx index 27a55f74b5..a8f5ec53de 100644 --- a/apps/www/src/registry/default/plate-ui/mention-element.tsx +++ b/apps/www/src/registry/default/plate-ui/mention-element.tsx @@ -27,6 +27,10 @@ export const MentionElement = withRef< element.children[0].underline === true && 'underline', className )} + /** + * TODO: Check why data-slate-value is present on mention input elements + * and mention and slash elements. + */ data-slate-value={element.value} contentEditable={false} onClick={getHandler(onClick, element)} diff --git a/apps/www/src/registry/default/plate-ui/mention-input-element.tsx b/apps/www/src/registry/default/plate-ui/mention-input-element.tsx index c59dc27efa..056e232e07 100644 --- a/apps/www/src/registry/default/plate-ui/mention-input-element.tsx +++ b/apps/www/src/registry/default/plate-ui/mention-input-element.tsx @@ -18,6 +18,10 @@ export const MentionInputElement = withRef< void; - } ->(({ className, onClick, ...props }, ref) => { - const { children, element } = props; +import React, { + HTMLAttributes, + RefObject, + useCallback, + useEffect, + useState, +} from 'react'; +import { + Combobox, + ComboboxItem, + ComboboxPopover, + ComboboxProvider, +} from '@ariakit/react'; +import { withRef } from '@udecode/cn'; +import { + findNodePath, + focusEditor, + insertText, + isHotkey, + moveSelection, + PlateEditor, + PlateElement, + removeNodes, + TElement, + useEditorRef, + useElement, +} from '@udecode/plate-common'; +import { useSelected } from 'slate-react'; + +const removeComboboxInput = (editor: PlateEditor, element: TElement) => { + const path = findNodePath(editor, element); + if (!path) return; + removeNodes(editor, { at: path }); +}; + +type ComboboxInputCursorState = { + atStart: boolean; + atEnd: boolean; +}; + +const useHTMLInputCursorState = ( + ref: RefObject +): ComboboxInputCursorState => { + const [cursorState, setCursorState] = useState({ + atStart: false, + atEnd: false, + }); + + const recomputeCursorState = useCallback(() => { + if (!ref.current) return; + + const { selectionStart, selectionEnd, value } = ref.current; + + setCursorState({ + atStart: selectionStart === 0, + atEnd: selectionEnd === value.length, + }); + }, [ref]); + + useEffect(() => { + recomputeCursorState(); + + const input = ref.current; + if (!input) return; + + input.addEventListener('input', recomputeCursorState); + input.addEventListener('selectionchange', recomputeCursorState); + return () => { + input.removeEventListener('input', recomputeCursorState); + input.removeEventListener('selectionchange', recomputeCursorState); + }; + }, [recomputeCursorState, ref]); + + return cursorState; +}; + +type CancelComboboxInputCause = + | 'manual' + | 'escape' + | 'backspace' + | 'arrowLeft' + | 'arrowRight' + | 'deselect' + | 'blur'; + +interface UseComboboxInputOptions { + ref: RefObject; + cursorState?: ComboboxInputCursorState; + autoFocus?: boolean; + cancelInputOnEscape?: boolean; + cancelInputOnBackspace?: boolean; + cancelInputOnArrowLeftRight?: boolean; + cancelInputOnDeselect?: boolean; + cancelInputOnBlur?: boolean; + onCancelInput?: (cause: CancelComboboxInputCause) => void; +} + +interface UseComboboxInputResult { + removeInput: (focusEditor?: boolean) => void; + cancelInput: ( + cause?: CancelComboboxInputCause, + focusEditor?: boolean + ) => void; + props: Required, 'onKeyDown' | 'onBlur'>>; +} + +const useComboboxInput = ({ + ref, + cursorState, + autoFocus = true, + cancelInputOnEscape = true, + cancelInputOnBackspace = true, + cancelInputOnArrowLeftRight = true, + cancelInputOnDeselect = true, + cancelInputOnBlur = true, + onCancelInput, +}: UseComboboxInputOptions): UseComboboxInputResult => { + const editor = useEditorRef(); + const element = useElement(); const selected = useSelected(); - const focused = useFocused(); - - return ( - - /{children} - + + const cursorAtStart = cursorState?.atStart ?? false; + const cursorAtEnd = cursorState?.atEnd ?? false; + + const removeInput = useCallback( + (shouldFocusEditor = false) => { + removeComboboxInput(editor, element); + + if (shouldFocusEditor) { + setTimeout(() => focusEditor(editor)); + } + }, + [editor, element] ); -}); + + const cancelInput = useCallback( + (cause: CancelComboboxInputCause = 'manual', shouldFocusEditor = false) => { + removeInput(shouldFocusEditor); + onCancelInput?.(cause); + }, + [onCancelInput, removeInput] + ); + + /** + * Using autoFocus on the input element causes an error: + * Cannot resolve a Slate node from DOM node: [object HTMLSpanElement] + */ + useEffect(() => { + if (autoFocus) { + ref.current?.focus(); + } + }, [autoFocus, ref]); + + useEffect(() => { + if (!selected && cancelInputOnDeselect) { + cancelInput('deselect'); + } + }, [selected, cancelInputOnDeselect, cancelInput]); + + return { + removeInput, + cancelInput, + props: { + onKeyDown: (event) => { + if (cancelInputOnEscape && isHotkey('escape', event)) { + cancelInput('escape', true); + } + + if ( + cancelInputOnBackspace && + cursorAtStart && + isHotkey('backspace', event) + ) { + cancelInput('backspace', true); + } + + if ( + cancelInputOnArrowLeftRight && + cursorAtStart && + isHotkey('arrowleft', event) + ) { + cancelInput('arrowLeft', true); + } + + if ( + cancelInputOnArrowLeftRight && + cursorAtEnd && + isHotkey('arrowright', event) + ) { + cancelInput('arrowRight', true); + } + }, + onBlur: () => { + if (cancelInputOnBlur) { + cancelInput('blur'); + } + }, + }, + }; +}; + +export const SlashInputElement = withRef( + ({ className, ...props }, ref) => { + const { children, element, editor } = props; + const inputRef = React.useRef(null); + + const cursorState = useHTMLInputCursorState(inputRef); + + const [value, setValue] = useState(''); + + const { removeInput, props: inputProps } = useComboboxInput({ + ref: inputRef, + cursorState, + cancelInputOnBlur: false, + onCancelInput: (cause) => { + if (cause !== 'backspace') { + insertText(editor, '/' + value); + } + + if (cause === 'arrowLeft' || cause === 'arrowRight') { + moveSelection(editor, { + distance: 1, + reverse: cause === 'arrowLeft', + }); + } + }, + }); + + return ( + + + / + { + removeInput(true); + insertText(editor, 'You selected: ' + selectedValue); + }} + > + + {/** + To create an auto-resizing input, we render a visually hidden span + containing the input value and position the input element on top of + it. This works well for all cases except when input exceeds the + width of the container. + */} + + + + + + + Apple + + Banana + + Cherry + + + + {children} + + ); + } +); diff --git a/packages/slash-command/src/createSlashPlugin.ts b/packages/slash-command/src/createSlashPlugin.ts index 2096d10a3d..9d2ebf204e 100644 --- a/packages/slash-command/src/createSlashPlugin.ts +++ b/packages/slash-command/src/createSlashPlugin.ts @@ -14,14 +14,14 @@ export const ELEMENT_SLASH_INPUT = 'slash_input'; export const createSlashPlugin = createPluginFactory({ key: KEY_SLASH_COMMAND, handlers: { - onKeyDown: slashOnKeyDownHandler({ query: isSelectionInSlashInput }), - onBlur: (editor) => () => { - // remove slash_input nodes from editor on blur - removeNodes(editor, { - match: (n) => n.type === ELEMENT_SLASH_INPUT, - at: [], - }); - }, + // onKeyDown: slashOnKeyDownHandler({ query: isSelectionInSlashInput }), + // onBlur: (editor) => () => { + // // remove slash_input nodes from editor on blur + // removeNodes(editor, { + // match: (n) => n.type === ELEMENT_SLASH_INPUT, + // at: [], + // }); + // }, }, withOverrides: withSlashCommand, options: { @@ -34,6 +34,7 @@ export const createSlashPlugin = createPluginFactory({ key: ELEMENT_SLASH_INPUT, isElement: true, isInline: true, + isVoid: true, }, ], then: (editor, { key }) => ({ diff --git a/packages/slash-command/src/withSlashCommand.ts b/packages/slash-command/src/withSlashCommand.ts index 9ac1ff2e76..19bc1552f8 100644 --- a/packages/slash-command/src/withSlashCommand.ts +++ b/packages/slash-command/src/withSlashCommand.ts @@ -45,64 +45,11 @@ export const withSlashCommand = < insertNode, } = editor; - const stripNewLineAndTrim: (text: string) => string = (text) => { - return text - .split(/\r\n|\r|\n/) - .map((line) => line.trim()) - .join(''); - }; - - editor.insertFragment = (fragment) => { - const inSlashInput = findSlashInput(editor) !== undefined; - if (!inSlashInput) { - return insertFragment(fragment); - } - - return insertText( - fragment.map((node) => stripNewLineAndTrim(getNodeString(node))).join('') - ); - }; - - editor.insertTextData = (data) => { - const inSlashInput = findSlashInput(editor) !== undefined; - if (!inSlashInput) { - return insertTextData(data); - } - - const text = data.getData('text/plain'); - if (!text) { - return false; - } - - editor.insertText(stripNewLineAndTrim(text)); - - return true; - }; - - editor.deleteBackward = (unit) => { - const currentSlashInput = findSlashInput(editor); - if (currentSlashInput && getNodeString(currentSlashInput[0]) === '') { - removeSlashInput(editor, currentSlashInput[1]); - return moveSelection(editor, { unit: 'word' }); - } - - deleteBackward(unit); - }; - - editor.insertBreak = () => { - if (isSelectionInSlashInput(editor)) { - return; - } - - insertBreak(); - }; - editor.insertText = (text) => { if ( !editor.selection || text !== trigger || - (query && !query(editor as PlateEditor)) || - isSelectionInSlashInput(editor) + (query && !query(editor as PlateEditor)) ) { return insertText(text); } @@ -138,69 +85,45 @@ export const withSlashCommand = < apply(operation); if (operation.type === 'insert_text' || operation.type === 'remove_text') { - const currentSlashInput = findSlashInput(editor); - if (currentSlashInput) { - comboboxActions.text(getNodeString(currentSlashInput[0])); - } } else if (operation.type === 'set_selection') { - const previousSlashInputPath = Range.isRange(operation.properties) - ? findSlashInput(editor, { - at: operation.properties, - })?.[1] - : undefined; - - const currentSlashInputPath = Range.isRange(operation.newProperties) - ? findSlashInput(editor, { - at: operation.newProperties, - })?.[1] - : undefined; - - if (previousSlashInputPath && !currentSlashInputPath) { - removeSlashInput(editor, previousSlashInputPath); - moveSelection(editor, { unit: 'word' }); - } - - if (currentSlashInputPath) { - comboboxActions.targetRange(editor.selection); - } } else if ( operation.type === 'insert_node' && isNodeSlashInput(editor, operation.node as TNode) ) { - if ((operation.node as TSlashInputElement).trigger !== trigger) { - return; - } - - const text = - ((operation.node as TSlashInputElement).children as TText[])[0]?.text ?? - ''; - - if ( - inputCreation === undefined || - operation.node[inputCreation.key] === inputCreation.value - ) { - // Needed for undo - after an undo a slash insert we only receive - // an insert_node with the slash input, i.e. nothing indicating that it - // was an undo. - setSelection(editor, { - anchor: { path: operation.path.concat([0]), offset: text.length }, - focus: { path: operation.path.concat([0]), offset: text.length }, - }); - - comboboxActions.open({ - activeId: id!, - text, - targetRange: editor.selection, - }); - } + // if ((operation.node as TSlashInputElement).trigger !== trigger) { + // return; + // } + + // const text = + // ((operation.node as TSlashInputElement).children as TText[])[0]?.text ?? + // ''; + + // if ( + // inputCreation === undefined || + // operation.node[inputCreation.key] === inputCreation.value + // ) { + // // Needed for undo - after an undo a slash insert we only receive + // // an insert_node with the slash input, i.e. nothing indicating that it + // // was an undo. + // setSelection(editor, { + // anchor: { path: operation.path.concat([0]), offset: text.length }, + // focus: { path: operation.path.concat([0]), offset: text.length }, + // }); + + // comboboxActions.open({ + // activeId: id!, + // text, + // targetRange: editor.selection, + // }); + // } } else if ( operation.type === 'remove_node' && isNodeSlashInput(editor, operation.node as TNode) ) { - if ((operation.node as TSlashInputElement).trigger !== trigger) { - return; - } - comboboxActions.reset(); + // if ((operation.node as TSlashInputElement).trigger !== trigger) { + // return; + // } + // comboboxActions.reset(); } }; diff --git a/yarn.lock b/yarn.lock index 99dccfa06e..572704dd90 100644 --- a/yarn.lock +++ b/yarn.lock @@ -51,6 +51,39 @@ __metadata: languageName: node linkType: hard +"@ariakit/core@npm:0.4.6": + version: 0.4.6 + resolution: "@ariakit/core@npm:0.4.6" + checksum: 10c0/ca95be5acfd55ad99fa2eaddfdcf2dd178622ac64634bec80709dc4c722f8f15ac6d321831c72ab034001fe00964f7a2531e519916f29cf885d8cf3ffdbb6776 + languageName: node + linkType: hard + +"@ariakit/react-core@npm:0.4.6": + version: 0.4.6 + resolution: "@ariakit/react-core@npm:0.4.6" + dependencies: + "@ariakit/core": "npm:0.4.6" + "@floating-ui/dom": "npm:^1.0.0" + use-sync-external-store: "npm:^1.2.0" + peerDependencies: + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + checksum: 10c0/cd24d020a380a5de48607119c7f46a1b64bc8780d7b4a18f09207b2a3c13957cfc1c5dc0256ad8dd0924714b074e30f4af5702d25b438885516d9c900cc8ce91 + languageName: node + linkType: hard + +"@ariakit/react@npm:0.4.6": + version: 0.4.6 + resolution: "@ariakit/react@npm:0.4.6" + dependencies: + "@ariakit/react-core": "npm:0.4.6" + peerDependencies: + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + checksum: 10c0/647d540c81d116de690e80544152471be59ced91ca1a31e81dbafea162397e3ce16844401eac708c06e8ad834ca6779eb373fc4634e28d6ecdb7e0f2fce4a061 + languageName: node + linkType: hard + "@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.21.4, @babel/code-frame@npm:^7.23.5, @babel/code-frame@npm:^7.24.1, @babel/code-frame@npm:^7.24.2": version: 7.24.2 resolution: "@babel/code-frame@npm:7.24.2" @@ -1535,6 +1568,16 @@ __metadata: languageName: node linkType: hard +"@floating-ui/dom@npm:^1.0.0": + version: 1.6.4 + resolution: "@floating-ui/dom@npm:1.6.4" + dependencies: + "@floating-ui/core": "npm:^1.0.0" + "@floating-ui/utils": "npm:^0.2.0" + checksum: 10c0/cee0b9e6efc1c6d978ec580c770078fdf416016fb03f3dd99630f7f32d0422722e608471fbc7578be86c783ad1c1e448c5fa5b9fdec889dfbf4b695f208730fd + languageName: node + linkType: hard + "@floating-ui/dom@npm:^1.2.1, @floating-ui/dom@npm:^1.6.1": version: 1.6.3 resolution: "@floating-ui/dom@npm:1.6.3" @@ -21300,6 +21343,7 @@ __metadata: version: 0.0.0-use.local resolution: "www@workspace:apps/www" dependencies: + "@ariakit/react": "npm:0.4.6" "@faker-js/faker": "npm:^8.4.1" "@radix-ui/colors": "npm:1.0.1" "@radix-ui/react-accessible-icon": "npm:^1.0.3" From 4aaf43cb7bf11aaa1bd3ae42d2b8e5c1ea947d7e Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Wed, 1 May 2024 17:10:51 +0100 Subject: [PATCH 04/63] Refactor combobox input hooks into package --- .../default/plate-ui/mention-element.tsx | 4 - .../plate-ui/mention-input-element.tsx | 4 - .../default/plate-ui/slash-input-element.tsx | 202 +----------------- packages/combobox/src/hooks/index.ts | 5 +- .../combobox/src/hooks/useComboboxInput.ts | 130 +++++++++++ .../src/hooks/useHTMLInputCursorState.ts | 40 ++++ packages/combobox/src/index.ts | 8 +- .../combobox.store.ts | 0 .../createComboboxPlugin.ts | 0 .../legacy-combobox-delete-me/hooks/index.ts | 7 + .../hooks/useComboboxContent.ts | 0 .../hooks/useComboboxControls.ts | 0 .../hooks/useComboboxItem.tsx | 0 .../src/legacy-combobox-delete-me/index.ts | 11 + .../onChangeCombobox.spec.tsx | 0 .../onChangeCombobox.ts | 0 .../onKeyDownCombobox.ts | 0 .../types/ComboboxOnSelectItem.ts | 0 .../types/ComboboxProps.ts | 0 .../types/index.ts | 0 .../utils/getNextNonDisabledIndex.ts | 0 .../utils/getNextWrappingIndex.ts | 0 .../utils/getTextFromTrigger.ts | 0 .../utils/index.ts | 0 packages/combobox/src/types.ts | 13 ++ 25 files changed, 210 insertions(+), 214 deletions(-) create mode 100644 packages/combobox/src/hooks/useComboboxInput.ts create mode 100644 packages/combobox/src/hooks/useHTMLInputCursorState.ts rename packages/combobox/src/{ => legacy-combobox-delete-me}/combobox.store.ts (100%) rename packages/combobox/src/{ => legacy-combobox-delete-me}/createComboboxPlugin.ts (100%) create mode 100644 packages/combobox/src/legacy-combobox-delete-me/hooks/index.ts rename packages/combobox/src/{ => legacy-combobox-delete-me}/hooks/useComboboxContent.ts (100%) rename packages/combobox/src/{ => legacy-combobox-delete-me}/hooks/useComboboxControls.ts (100%) rename packages/combobox/src/{ => legacy-combobox-delete-me}/hooks/useComboboxItem.tsx (100%) create mode 100644 packages/combobox/src/legacy-combobox-delete-me/index.ts rename packages/combobox/src/{ => legacy-combobox-delete-me}/onChangeCombobox.spec.tsx (100%) rename packages/combobox/src/{ => legacy-combobox-delete-me}/onChangeCombobox.ts (100%) rename packages/combobox/src/{ => legacy-combobox-delete-me}/onKeyDownCombobox.ts (100%) rename packages/combobox/src/{ => legacy-combobox-delete-me}/types/ComboboxOnSelectItem.ts (100%) rename packages/combobox/src/{ => legacy-combobox-delete-me}/types/ComboboxProps.ts (100%) rename packages/combobox/src/{ => legacy-combobox-delete-me}/types/index.ts (100%) rename packages/combobox/src/{ => legacy-combobox-delete-me}/utils/getNextNonDisabledIndex.ts (100%) rename packages/combobox/src/{ => legacy-combobox-delete-me}/utils/getNextWrappingIndex.ts (100%) rename packages/combobox/src/{ => legacy-combobox-delete-me}/utils/getTextFromTrigger.ts (100%) rename packages/combobox/src/{ => legacy-combobox-delete-me}/utils/index.ts (100%) create mode 100644 packages/combobox/src/types.ts diff --git a/apps/www/src/registry/default/plate-ui/mention-element.tsx b/apps/www/src/registry/default/plate-ui/mention-element.tsx index a8f5ec53de..27a55f74b5 100644 --- a/apps/www/src/registry/default/plate-ui/mention-element.tsx +++ b/apps/www/src/registry/default/plate-ui/mention-element.tsx @@ -27,10 +27,6 @@ export const MentionElement = withRef< element.children[0].underline === true && 'underline', className )} - /** - * TODO: Check why data-slate-value is present on mention input elements - * and mention and slash elements. - */ data-slate-value={element.value} contentEditable={false} onClick={getHandler(onClick, element)} diff --git a/apps/www/src/registry/default/plate-ui/mention-input-element.tsx b/apps/www/src/registry/default/plate-ui/mention-input-element.tsx index 056e232e07..c59dc27efa 100644 --- a/apps/www/src/registry/default/plate-ui/mention-input-element.tsx +++ b/apps/www/src/registry/default/plate-ui/mention-input-element.tsx @@ -18,10 +18,6 @@ export const MentionInputElement = withRef< { - const path = findNodePath(editor, element); - if (!path) return; - removeNodes(editor, { at: path }); -}; - -type ComboboxInputCursorState = { - atStart: boolean; - atEnd: boolean; -}; - -const useHTMLInputCursorState = ( - ref: RefObject -): ComboboxInputCursorState => { - const [cursorState, setCursorState] = useState({ - atStart: false, - atEnd: false, - }); - - const recomputeCursorState = useCallback(() => { - if (!ref.current) return; - - const { selectionStart, selectionEnd, value } = ref.current; - - setCursorState({ - atStart: selectionStart === 0, - atEnd: selectionEnd === value.length, - }); - }, [ref]); - - useEffect(() => { - recomputeCursorState(); - - const input = ref.current; - if (!input) return; - - input.addEventListener('input', recomputeCursorState); - input.addEventListener('selectionchange', recomputeCursorState); - - return () => { - input.removeEventListener('input', recomputeCursorState); - input.removeEventListener('selectionchange', recomputeCursorState); - }; - }, [recomputeCursorState, ref]); - - return cursorState; -}; - -type CancelComboboxInputCause = - | 'manual' - | 'escape' - | 'backspace' - | 'arrowLeft' - | 'arrowRight' - | 'deselect' - | 'blur'; - -interface UseComboboxInputOptions { - ref: RefObject; - cursorState?: ComboboxInputCursorState; - autoFocus?: boolean; - cancelInputOnEscape?: boolean; - cancelInputOnBackspace?: boolean; - cancelInputOnArrowLeftRight?: boolean; - cancelInputOnDeselect?: boolean; - cancelInputOnBlur?: boolean; - onCancelInput?: (cause: CancelComboboxInputCause) => void; -} - -interface UseComboboxInputResult { - removeInput: (focusEditor?: boolean) => void; - cancelInput: ( - cause?: CancelComboboxInputCause, - focusEditor?: boolean - ) => void; - props: Required, 'onKeyDown' | 'onBlur'>>; -} - -const useComboboxInput = ({ - ref, - cursorState, - autoFocus = true, - cancelInputOnEscape = true, - cancelInputOnBackspace = true, - cancelInputOnArrowLeftRight = true, - cancelInputOnDeselect = true, - cancelInputOnBlur = true, - onCancelInput, -}: UseComboboxInputOptions): UseComboboxInputResult => { - const editor = useEditorRef(); - const element = useElement(); - const selected = useSelected(); - - const cursorAtStart = cursorState?.atStart ?? false; - const cursorAtEnd = cursorState?.atEnd ?? false; - - const removeInput = useCallback( - (shouldFocusEditor = false) => { - removeComboboxInput(editor, element); - - if (shouldFocusEditor) { - setTimeout(() => focusEditor(editor)); - } - }, - [editor, element] - ); - - const cancelInput = useCallback( - (cause: CancelComboboxInputCause = 'manual', shouldFocusEditor = false) => { - removeInput(shouldFocusEditor); - onCancelInput?.(cause); - }, - [onCancelInput, removeInput] - ); - - /** - * Using autoFocus on the input element causes an error: - * Cannot resolve a Slate node from DOM node: [object HTMLSpanElement] - */ - useEffect(() => { - if (autoFocus) { - ref.current?.focus(); - } - }, [autoFocus, ref]); - - useEffect(() => { - if (!selected && cancelInputOnDeselect) { - cancelInput('deselect'); - } - }, [selected, cancelInputOnDeselect, cancelInput]); - - return { - removeInput, - cancelInput, - props: { - onKeyDown: (event) => { - if (cancelInputOnEscape && isHotkey('escape', event)) { - cancelInput('escape', true); - } - - if ( - cancelInputOnBackspace && - cursorAtStart && - isHotkey('backspace', event) - ) { - cancelInput('backspace', true); - } - - if ( - cancelInputOnArrowLeftRight && - cursorAtStart && - isHotkey('arrowleft', event) - ) { - cancelInput('arrowLeft', true); - } - - if ( - cancelInputOnArrowLeftRight && - cursorAtEnd && - isHotkey('arrowright', event) - ) { - cancelInput('arrowRight', true); - } - }, - onBlur: () => { - if (cancelInputOnBlur) { - cancelInput('blur'); - } - }, - }, - }; -}; + useComboboxInput, + useHTMLInputCursorState, +} from '@udecode/plate-combobox'; +import { insertText, moveSelection, PlateElement } from '@udecode/plate-common'; export const SlashInputElement = withRef( ({ className, ...props }, ref) => { @@ -231,10 +43,6 @@ export const SlashInputElement = withRef( diff --git a/packages/combobox/src/hooks/index.ts b/packages/combobox/src/hooks/index.ts index 2426758fb2..ff33da9b7e 100644 --- a/packages/combobox/src/hooks/index.ts +++ b/packages/combobox/src/hooks/index.ts @@ -2,6 +2,5 @@ * @file Automatically generated by barrelsby. */ -export * from './useComboboxContent'; -export * from './useComboboxControls'; -export * from './useComboboxItem'; +export * from './useComboboxInput'; +export * from './useHTMLInputCursorState'; diff --git a/packages/combobox/src/hooks/useComboboxInput.ts b/packages/combobox/src/hooks/useComboboxInput.ts new file mode 100644 index 0000000000..0183f6d7ad --- /dev/null +++ b/packages/combobox/src/hooks/useComboboxInput.ts @@ -0,0 +1,130 @@ +import { HTMLAttributes, RefObject, useCallback, useEffect } from 'react'; +import { + findNodePath, + focusEditor, + isHotkey, + removeNodes, + useEditorRef, + useElement, +} from '@udecode/plate-common'; +import { useSelected } from 'slate-react'; + +import { CancelComboboxInputCause, ComboboxInputCursorState } from '../types'; + +export interface UseComboboxInputOptions { + ref: RefObject; + cursorState?: ComboboxInputCursorState; + autoFocus?: boolean; + cancelInputOnEscape?: boolean; + cancelInputOnBackspace?: boolean; + cancelInputOnArrowLeftRight?: boolean; + cancelInputOnDeselect?: boolean; + cancelInputOnBlur?: boolean; + onCancelInput?: (cause: CancelComboboxInputCause) => void; +} + +export interface UseComboboxInputResult { + removeInput: (focusEditor?: boolean) => void; + cancelInput: ( + cause?: CancelComboboxInputCause, + focusEditor?: boolean + ) => void; + props: Required, 'onKeyDown' | 'onBlur'>>; +} + +export const useComboboxInput = ({ + ref, + cursorState, + autoFocus = true, + cancelInputOnEscape = true, + cancelInputOnBackspace = true, + cancelInputOnArrowLeftRight = true, + cancelInputOnDeselect = true, + cancelInputOnBlur = true, + onCancelInput, +}: UseComboboxInputOptions): UseComboboxInputResult => { + const editor = useEditorRef(); + const element = useElement(); + const selected = useSelected(); + + const cursorAtStart = cursorState?.atStart ?? false; + const cursorAtEnd = cursorState?.atEnd ?? false; + + const removeInput = useCallback( + (shouldFocusEditor = false) => { + const path = findNodePath(editor, element); + if (!path) return; + removeNodes(editor, { at: path }); + + if (shouldFocusEditor) { + setTimeout(() => focusEditor(editor)); + } + }, + [editor, element] + ); + + const cancelInput = useCallback( + (cause: CancelComboboxInputCause = 'manual', shouldFocusEditor = false) => { + removeInput(shouldFocusEditor); + onCancelInput?.(cause); + }, + [onCancelInput, removeInput] + ); + + /** + * Using autoFocus on the input element causes an error: + * Cannot resolve a Slate node from DOM node: [object HTMLSpanElement] + */ + useEffect(() => { + if (autoFocus) { + ref.current?.focus(); + } + }, [autoFocus, ref]); + + useEffect(() => { + if (!selected && cancelInputOnDeselect) { + cancelInput('deselect'); + } + }, [selected, cancelInputOnDeselect, cancelInput]); + + return { + removeInput, + cancelInput, + props: { + onKeyDown: (event) => { + if (cancelInputOnEscape && isHotkey('escape', event)) { + cancelInput('escape', true); + } + + if ( + cancelInputOnBackspace && + cursorAtStart && + isHotkey('backspace', event) + ) { + cancelInput('backspace', true); + } + + if ( + cancelInputOnArrowLeftRight && + cursorAtStart && + isHotkey('arrowleft', event) + ) { + cancelInput('arrowLeft', true); + } + + if ( + cancelInputOnArrowLeftRight && + cursorAtEnd && + isHotkey('arrowright', event) + ) { + cancelInput('arrowRight', true); + } + }, + onBlur: () => { + if (cancelInputOnBlur) { + cancelInput('blur'); + } + }, + }, + }; +}; diff --git a/packages/combobox/src/hooks/useHTMLInputCursorState.ts b/packages/combobox/src/hooks/useHTMLInputCursorState.ts new file mode 100644 index 0000000000..3655347c6d --- /dev/null +++ b/packages/combobox/src/hooks/useHTMLInputCursorState.ts @@ -0,0 +1,40 @@ +import { RefObject, useCallback, useEffect, useState } from 'react'; + +import { ComboboxInputCursorState } from '../types'; + +export const useHTMLInputCursorState = ( + ref: RefObject +): ComboboxInputCursorState => { + const [cursorState, setCursorState] = useState({ + atStart: false, + atEnd: false, + }); + + const recomputeCursorState = useCallback(() => { + if (!ref.current) return; + + const { selectionStart, selectionEnd, value } = ref.current; + + setCursorState({ + atStart: selectionStart === 0, + atEnd: selectionEnd === value.length, + }); + }, [ref]); + + useEffect(() => { + recomputeCursorState(); + + const input = ref.current; + if (!input) return; + + input.addEventListener('input', recomputeCursorState); + input.addEventListener('selectionchange', recomputeCursorState); + + return () => { + input.removeEventListener('input', recomputeCursorState); + input.removeEventListener('selectionchange', recomputeCursorState); + }; + }, [recomputeCursorState, ref]); + + return cursorState; +}; diff --git a/packages/combobox/src/index.ts b/packages/combobox/src/index.ts index a198a8b6e5..862e797335 100644 --- a/packages/combobox/src/index.ts +++ b/packages/combobox/src/index.ts @@ -2,10 +2,6 @@ * @file Automatically generated by barrelsby. */ -export * from './combobox.store'; -export * from './createComboboxPlugin'; -export * from './onChangeCombobox'; -export * from './onKeyDownCombobox'; +export * from './types'; export * from './hooks/index'; -export * from './types/index'; -export * from './utils/index'; +export * from './legacy-combobox-delete-me/index'; diff --git a/packages/combobox/src/combobox.store.ts b/packages/combobox/src/legacy-combobox-delete-me/combobox.store.ts similarity index 100% rename from packages/combobox/src/combobox.store.ts rename to packages/combobox/src/legacy-combobox-delete-me/combobox.store.ts diff --git a/packages/combobox/src/createComboboxPlugin.ts b/packages/combobox/src/legacy-combobox-delete-me/createComboboxPlugin.ts similarity index 100% rename from packages/combobox/src/createComboboxPlugin.ts rename to packages/combobox/src/legacy-combobox-delete-me/createComboboxPlugin.ts diff --git a/packages/combobox/src/legacy-combobox-delete-me/hooks/index.ts b/packages/combobox/src/legacy-combobox-delete-me/hooks/index.ts new file mode 100644 index 0000000000..2426758fb2 --- /dev/null +++ b/packages/combobox/src/legacy-combobox-delete-me/hooks/index.ts @@ -0,0 +1,7 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './useComboboxContent'; +export * from './useComboboxControls'; +export * from './useComboboxItem'; diff --git a/packages/combobox/src/hooks/useComboboxContent.ts b/packages/combobox/src/legacy-combobox-delete-me/hooks/useComboboxContent.ts similarity index 100% rename from packages/combobox/src/hooks/useComboboxContent.ts rename to packages/combobox/src/legacy-combobox-delete-me/hooks/useComboboxContent.ts diff --git a/packages/combobox/src/hooks/useComboboxControls.ts b/packages/combobox/src/legacy-combobox-delete-me/hooks/useComboboxControls.ts similarity index 100% rename from packages/combobox/src/hooks/useComboboxControls.ts rename to packages/combobox/src/legacy-combobox-delete-me/hooks/useComboboxControls.ts diff --git a/packages/combobox/src/hooks/useComboboxItem.tsx b/packages/combobox/src/legacy-combobox-delete-me/hooks/useComboboxItem.tsx similarity index 100% rename from packages/combobox/src/hooks/useComboboxItem.tsx rename to packages/combobox/src/legacy-combobox-delete-me/hooks/useComboboxItem.tsx diff --git a/packages/combobox/src/legacy-combobox-delete-me/index.ts b/packages/combobox/src/legacy-combobox-delete-me/index.ts new file mode 100644 index 0000000000..a198a8b6e5 --- /dev/null +++ b/packages/combobox/src/legacy-combobox-delete-me/index.ts @@ -0,0 +1,11 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './combobox.store'; +export * from './createComboboxPlugin'; +export * from './onChangeCombobox'; +export * from './onKeyDownCombobox'; +export * from './hooks/index'; +export * from './types/index'; +export * from './utils/index'; diff --git a/packages/combobox/src/onChangeCombobox.spec.tsx b/packages/combobox/src/legacy-combobox-delete-me/onChangeCombobox.spec.tsx similarity index 100% rename from packages/combobox/src/onChangeCombobox.spec.tsx rename to packages/combobox/src/legacy-combobox-delete-me/onChangeCombobox.spec.tsx diff --git a/packages/combobox/src/onChangeCombobox.ts b/packages/combobox/src/legacy-combobox-delete-me/onChangeCombobox.ts similarity index 100% rename from packages/combobox/src/onChangeCombobox.ts rename to packages/combobox/src/legacy-combobox-delete-me/onChangeCombobox.ts diff --git a/packages/combobox/src/onKeyDownCombobox.ts b/packages/combobox/src/legacy-combobox-delete-me/onKeyDownCombobox.ts similarity index 100% rename from packages/combobox/src/onKeyDownCombobox.ts rename to packages/combobox/src/legacy-combobox-delete-me/onKeyDownCombobox.ts diff --git a/packages/combobox/src/types/ComboboxOnSelectItem.ts b/packages/combobox/src/legacy-combobox-delete-me/types/ComboboxOnSelectItem.ts similarity index 100% rename from packages/combobox/src/types/ComboboxOnSelectItem.ts rename to packages/combobox/src/legacy-combobox-delete-me/types/ComboboxOnSelectItem.ts diff --git a/packages/combobox/src/types/ComboboxProps.ts b/packages/combobox/src/legacy-combobox-delete-me/types/ComboboxProps.ts similarity index 100% rename from packages/combobox/src/types/ComboboxProps.ts rename to packages/combobox/src/legacy-combobox-delete-me/types/ComboboxProps.ts diff --git a/packages/combobox/src/types/index.ts b/packages/combobox/src/legacy-combobox-delete-me/types/index.ts similarity index 100% rename from packages/combobox/src/types/index.ts rename to packages/combobox/src/legacy-combobox-delete-me/types/index.ts diff --git a/packages/combobox/src/utils/getNextNonDisabledIndex.ts b/packages/combobox/src/legacy-combobox-delete-me/utils/getNextNonDisabledIndex.ts similarity index 100% rename from packages/combobox/src/utils/getNextNonDisabledIndex.ts rename to packages/combobox/src/legacy-combobox-delete-me/utils/getNextNonDisabledIndex.ts diff --git a/packages/combobox/src/utils/getNextWrappingIndex.ts b/packages/combobox/src/legacy-combobox-delete-me/utils/getNextWrappingIndex.ts similarity index 100% rename from packages/combobox/src/utils/getNextWrappingIndex.ts rename to packages/combobox/src/legacy-combobox-delete-me/utils/getNextWrappingIndex.ts diff --git a/packages/combobox/src/utils/getTextFromTrigger.ts b/packages/combobox/src/legacy-combobox-delete-me/utils/getTextFromTrigger.ts similarity index 100% rename from packages/combobox/src/utils/getTextFromTrigger.ts rename to packages/combobox/src/legacy-combobox-delete-me/utils/getTextFromTrigger.ts diff --git a/packages/combobox/src/utils/index.ts b/packages/combobox/src/legacy-combobox-delete-me/utils/index.ts similarity index 100% rename from packages/combobox/src/utils/index.ts rename to packages/combobox/src/legacy-combobox-delete-me/utils/index.ts diff --git a/packages/combobox/src/types.ts b/packages/combobox/src/types.ts new file mode 100644 index 0000000000..7e8b6a5b90 --- /dev/null +++ b/packages/combobox/src/types.ts @@ -0,0 +1,13 @@ +export type ComboboxInputCursorState = { + atStart: boolean; + atEnd: boolean; +}; + +export type CancelComboboxInputCause = + | 'manual' + | 'escape' + | 'backspace' + | 'arrowLeft' + | 'arrowRight' + | 'deselect' + | 'blur'; From 71bbfced5cec63b4876aeea789b98117e9d69cba Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Wed, 1 May 2024 17:43:05 +0100 Subject: [PATCH 05/63] Refactor `createSlashPlugin` to use combobox helper --- packages/combobox/src/index.ts | 1 + packages/combobox/src/types.ts | 11 ++ .../src/withInsertTextTriggerCombobox.ts | 65 +++++++++ .../slash-command/src/createSlashPlugin.ts | 39 ++---- .../slash-command/src/getSlashOnSelectItem.ts | 80 ----------- packages/slash-command/src/handlers/index.ts | 5 - .../src/handlers/slashOnKeyDownHandler.ts | 30 ---- packages/slash-command/src/index.ts | 5 - .../src/queries/findSlashInput.ts | 19 --- packages/slash-command/src/queries/index.ts | 7 - .../src/queries/isNodeSlashInput.ts | 16 --- .../src/queries/isSelectionInSlashInput.ts | 7 - .../slash-command/src/transforms/index.ts | 5 - .../src/transforms/removeSlashInput.ts | 30 ---- packages/slash-command/src/types.ts | 21 +-- .../slash-command/src/withSlashCommand.ts | 131 ------------------ 16 files changed, 92 insertions(+), 380 deletions(-) create mode 100644 packages/combobox/src/withInsertTextTriggerCombobox.ts delete mode 100644 packages/slash-command/src/getSlashOnSelectItem.ts delete mode 100644 packages/slash-command/src/handlers/index.ts delete mode 100644 packages/slash-command/src/handlers/slashOnKeyDownHandler.ts delete mode 100644 packages/slash-command/src/queries/findSlashInput.ts delete mode 100644 packages/slash-command/src/queries/index.ts delete mode 100644 packages/slash-command/src/queries/isNodeSlashInput.ts delete mode 100644 packages/slash-command/src/queries/isSelectionInSlashInput.ts delete mode 100644 packages/slash-command/src/transforms/index.ts delete mode 100644 packages/slash-command/src/transforms/removeSlashInput.ts delete mode 100644 packages/slash-command/src/withSlashCommand.ts diff --git a/packages/combobox/src/index.ts b/packages/combobox/src/index.ts index 862e797335..74fb607813 100644 --- a/packages/combobox/src/index.ts +++ b/packages/combobox/src/index.ts @@ -3,5 +3,6 @@ */ export * from './types'; +export * from './withInsertTextTriggerCombobox'; export * from './hooks/index'; export * from './legacy-combobox-delete-me/index'; diff --git a/packages/combobox/src/types.ts b/packages/combobox/src/types.ts index 7e8b6a5b90..7c4bdb2432 100644 --- a/packages/combobox/src/types.ts +++ b/packages/combobox/src/types.ts @@ -1,3 +1,14 @@ +import {PlateEditor, TElement} from "@udecode/plate-common"; + +export interface TriggerComboboxPlugin { + combobox?: { + trigger?: string; + triggerPreviousCharPattern?: RegExp; + query?: (editor: PlateEditor) => boolean; + createInputNode?: () => TElement; + }; +} + export type ComboboxInputCursorState = { atStart: boolean; atEnd: boolean; diff --git a/packages/combobox/src/withInsertTextTriggerCombobox.ts b/packages/combobox/src/withInsertTextTriggerCombobox.ts new file mode 100644 index 0000000000..2e4b4abf26 --- /dev/null +++ b/packages/combobox/src/withInsertTextTriggerCombobox.ts @@ -0,0 +1,65 @@ +import { + getEditorString, + getPointBefore, + getRange, + PlateEditor, + TElement, + Value, + WithPlatePlugin, +} from '@udecode/plate-common'; +import {TriggerComboboxPlugin} from './types'; + +export const withInsertTextTriggerCombobox = < + V extends Value = Value, + E extends PlateEditor = PlateEditor, +>( + editor: E, + { + type, + options: { + combobox: { + trigger, + triggerPreviousCharPattern, + query, + createInputNode, + } = {}, + }, + }: WithPlatePlugin +) => { + const { insertText } = editor; + + editor.insertText = (text) => { + if ( + !editor.selection || + text !== trigger || + (query && !query(editor as PlateEditor)) + ) { + return insertText(text); + } + + // Make sure an input is created at the beginning of line or after a whitespace + const previousChar = getEditorString( + editor, + getRange( + editor, + editor.selection, + getPointBefore(editor, editor.selection) + ) + ); + + const matchesPreviousCharPattern = + triggerPreviousCharPattern?.test(previousChar); + + if (matchesPreviousCharPattern && text === trigger) { + const inputNode: TElement = createInputNode + ? createInputNode() + : { type, children: [{ text: '' }] }; + + return editor.insertNode(inputNode); + } + + return insertText(text); + }; + + return editor; +}; diff --git a/packages/slash-command/src/createSlashPlugin.ts b/packages/slash-command/src/createSlashPlugin.ts index 9d2ebf204e..ebf7c2d06a 100644 --- a/packages/slash-command/src/createSlashPlugin.ts +++ b/packages/slash-command/src/createSlashPlugin.ts @@ -1,34 +1,14 @@ -import { createPluginFactory, removeNodes } from '@udecode/plate-common'; +import { withInsertTextTriggerCombobox } from '@udecode/plate-combobox'; +import { createPluginFactory } from '@udecode/plate-common'; -import { slashOnKeyDownHandler } from './handlers/slashOnKeyDownHandler'; -import { isSelectionInSlashInput } from './queries/index'; import { SlashPlugin } from './types'; -import { withSlashCommand } from './withSlashCommand'; export const KEY_SLASH_COMMAND = 'slash_command'; export const ELEMENT_SLASH_INPUT = 'slash_input'; -/** - * Enables support for autocompleting /slash_command. - */ export const createSlashPlugin = createPluginFactory({ key: KEY_SLASH_COMMAND, - handlers: { - // onKeyDown: slashOnKeyDownHandler({ query: isSelectionInSlashInput }), - // onBlur: (editor) => () => { - // // remove slash_input nodes from editor on blur - // removeNodes(editor, { - // match: (n) => n.type === ELEMENT_SLASH_INPUT, - // at: [], - // }); - // }, - }, - withOverrides: withSlashCommand, - options: { - trigger: '/', - triggerPreviousCharPattern: /^\s?$/, - createSlashNode: (item) => ({ value: item.text }), - }, + withOverrides: withInsertTextTriggerCombobox, plugins: [ { key: ELEMENT_SLASH_INPUT, @@ -37,9 +17,14 @@ export const createSlashPlugin = createPluginFactory({ isVoid: true, }, ], - then: (editor, { key }) => ({ - options: { - id: key, + options: { + combobox: { + trigger: '/', + triggerPreviousCharPattern: /^\s?$/, + createInputNode: () => ({ + type: ELEMENT_SLASH_INPUT, + children: [{ text: '' }], + }), }, - }), + }, }); diff --git a/packages/slash-command/src/getSlashOnSelectItem.ts b/packages/slash-command/src/getSlashOnSelectItem.ts deleted file mode 100644 index 6a9830f242..0000000000 --- a/packages/slash-command/src/getSlashOnSelectItem.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { - comboboxActions, - ComboboxOnSelectItem, - comboboxSelectors, - Data, - NoData, - TComboboxItem, -} from '@udecode/plate-combobox'; -import { - getBlockAbove, - getPlugin, - insertText, - isEndPoint, - moveSelection, - PlatePluginKey, - removeNodes, - TNodeProps, - withoutMergingHistory, - withoutNormalizing, -} from '@udecode/plate-common'; - -import { KEY_SLASH_COMMAND } from './createSlashPlugin'; -import { isNodeSlashInput } from './queries/isNodeSlashInput'; -import { SlashPlugin, TSlashElement } from './types'; - -export interface CreateSlashNode { - ( - item: TComboboxItem, - meta: CreateSlashNodeMeta - ): TNodeProps; -} - -export interface CreateSlashNodeMeta { - search: string; -} - -export const getSlashOnSelectItem = - ({ - key = KEY_SLASH_COMMAND, - }: PlatePluginKey = {}): ComboboxOnSelectItem => - (editor: any, item: any) => { - const targetRange = comboboxSelectors.targetRange(); - if (!targetRange) return; - - const { - options: { rules, insertSpaceAfterSlash }, - } = getPlugin(editor, key); - - const pathAbove = getBlockAbove(editor)?.[1]; - const isBlockEnd = () => - editor.selection && - pathAbove && - isEndPoint(editor, editor.selection.anchor, pathAbove); - - withoutNormalizing(editor, () => { - // Selectors are sensitive to operations, it's better to create everything - // before the editor state is changed. For example, asking for text after - // removeNodes below will return null. - - withoutMergingHistory(editor, () => - removeNodes(editor, { - match: (node) => isNodeSlashInput(editor, node), - }) - ); - - if (rules) { - const target = rules.find((rule) => rule.key === item.key); - target && target.onTrigger(editor, target.key); - } - - // move the selection after the element - moveSelection(editor, { unit: 'offset' }); - - if (isBlockEnd() && insertSpaceAfterSlash) { - insertText(editor, ' '); - } - }); - - return comboboxActions.reset(); - }; diff --git a/packages/slash-command/src/handlers/index.ts b/packages/slash-command/src/handlers/index.ts deleted file mode 100644 index ded3e12b8f..0000000000 --- a/packages/slash-command/src/handlers/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** - * @file Automatically generated by barrelsby. - */ - -export * from './slashOnKeyDownHandler'; diff --git a/packages/slash-command/src/handlers/slashOnKeyDownHandler.ts b/packages/slash-command/src/handlers/slashOnKeyDownHandler.ts deleted file mode 100644 index b4fdd781c9..0000000000 --- a/packages/slash-command/src/handlers/slashOnKeyDownHandler.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { - isHotkey, - KeyboardEventHandler, - moveSelection, - moveSelectionByOffset, - MoveSelectionByOffsetOptions, - PlateEditor, - Value, -} from '@udecode/plate-common'; - -import { findSlashInput } from '../queries/index'; -import { removeSlashInput } from '../transforms/index'; - -export const slashOnKeyDownHandler: ( - options?: MoveSelectionByOffsetOptions -) => (editor: PlateEditor) => KeyboardEventHandler = - (options) => (editor) => (event) => { - if (isHotkey('escape', event)) { - const currentSlashInput = findSlashInput(editor)!; - if (currentSlashInput) { - event.preventDefault(); - removeSlashInput(editor, currentSlashInput[1]); - moveSelection(editor, { unit: 'word' }); - return true; - } - return false; - } - - return moveSelectionByOffset(editor, options)(event); - }; diff --git a/packages/slash-command/src/index.ts b/packages/slash-command/src/index.ts index c8f331807d..81e2206b22 100644 --- a/packages/slash-command/src/index.ts +++ b/packages/slash-command/src/index.ts @@ -3,9 +3,4 @@ */ export * from './createSlashPlugin'; -export * from './getSlashOnSelectItem'; export * from './types'; -export * from './withSlashCommand'; -export * from './handlers/index'; -export * from './queries/index'; -export * from './transforms/index'; diff --git a/packages/slash-command/src/queries/findSlashInput.ts b/packages/slash-command/src/queries/findSlashInput.ts deleted file mode 100644 index 12a1072c66..0000000000 --- a/packages/slash-command/src/queries/findSlashInput.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { - findNode, - FindNodeOptions, - getPluginType, - PlateEditor, - Value, -} from '@udecode/plate-common'; - -import { ELEMENT_SLASH_INPUT } from '../createSlashPlugin'; -import { TSlashInputElement } from '../types'; - -export const findSlashInput = ( - editor: PlateEditor, - options?: Omit, 'match'> -) => - findNode(editor, { - ...options, - match: { type: getPluginType(editor, ELEMENT_SLASH_INPUT) }, - }); diff --git a/packages/slash-command/src/queries/index.ts b/packages/slash-command/src/queries/index.ts deleted file mode 100644 index e43bb26031..0000000000 --- a/packages/slash-command/src/queries/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * @file Automatically generated by barrelsby. - */ - -export * from './findSlashInput'; -export * from './isNodeSlashInput'; -export * from './isSelectionInSlashInput'; diff --git a/packages/slash-command/src/queries/isNodeSlashInput.ts b/packages/slash-command/src/queries/isNodeSlashInput.ts deleted file mode 100644 index 89609de94f..0000000000 --- a/packages/slash-command/src/queries/isNodeSlashInput.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { - getPluginType, - PlateEditor, - TNode, - Value, -} from '@udecode/plate-common'; - -import { ELEMENT_SLASH_INPUT } from '../createSlashPlugin'; -import { TSlashInputElement } from '../types'; - -export const isNodeSlashInput = ( - editor: PlateEditor, - node: TNode -): node is TSlashInputElement => { - return node.type === getPluginType(editor, ELEMENT_SLASH_INPUT); -}; diff --git a/packages/slash-command/src/queries/isSelectionInSlashInput.ts b/packages/slash-command/src/queries/isSelectionInSlashInput.ts deleted file mode 100644 index 05dec5a44d..0000000000 --- a/packages/slash-command/src/queries/isSelectionInSlashInput.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { PlateEditor, Value } from '@udecode/plate-common'; - -import { findSlashInput } from '.'; - -export const isSelectionInSlashInput = ( - editor: PlateEditor -) => findSlashInput(editor) !== undefined; diff --git a/packages/slash-command/src/transforms/index.ts b/packages/slash-command/src/transforms/index.ts deleted file mode 100644 index acf25b6224..0000000000 --- a/packages/slash-command/src/transforms/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** - * @file Automatically generated by barrelsby. - */ - -export * from './removeSlashInput'; diff --git a/packages/slash-command/src/transforms/removeSlashInput.ts b/packages/slash-command/src/transforms/removeSlashInput.ts deleted file mode 100644 index 36bbd4b278..0000000000 --- a/packages/slash-command/src/transforms/removeSlashInput.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { - EText, - getNode, - getNodeString, - PlateEditor, - replaceNode, - Value, - withoutNormalizing, -} from '@udecode/plate-common'; -import { Path } from 'slate'; - -import { TSlashInputElement } from '../types'; - -export const removeSlashInput = ( - editor: PlateEditor, - path: Path -) => - withoutNormalizing(editor, () => { - const node = getNode(editor, path); - if (!node) return; - - const { trigger } = node; - - const text = getNodeString(node); - - replaceNode(editor, { - at: path, - nodes: { text: `${trigger}${text}` } as EText, - }); - }); diff --git a/packages/slash-command/src/types.ts b/packages/slash-command/src/types.ts index c7a73a709f..f08d4a93ce 100644 --- a/packages/slash-command/src/types.ts +++ b/packages/slash-command/src/types.ts @@ -1,15 +1,7 @@ -import { Data, NoData } from '@udecode/plate-combobox'; +import { TriggerComboboxPlugin } from '@udecode/plate-combobox'; import { PlateEditor, TElement } from '@udecode/plate-common'; -import { CreateSlashNode } from './getSlashOnSelectItem'; - -export interface TSlashElement extends TElement { - value: string; -} - -export interface TSlashInputElement extends TElement { - trigger: string; -} +export interface TSlashInputElement extends TElement {} export interface SlashRule { key: string; @@ -17,13 +9,6 @@ export interface SlashRule { onTrigger: (editor: PlateEditor, key: string) => void; } -export interface SlashPlugin { - createSlashNode?: CreateSlashNode; - id?: string; - insertSpaceAfterSlash?: boolean; - trigger?: string; - triggerPreviousCharPattern?: RegExp; - inputCreation?: { key: string; value: string }; - query?: (editor: PlateEditor) => boolean; +export interface SlashPlugin extends TriggerComboboxPlugin { rules?: SlashRule[]; } diff --git a/packages/slash-command/src/withSlashCommand.ts b/packages/slash-command/src/withSlashCommand.ts deleted file mode 100644 index 19bc1552f8..0000000000 --- a/packages/slash-command/src/withSlashCommand.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { comboboxActions } from '@udecode/plate-combobox'; -import { - getEditorString, - getNodeString, - getPlugin, - getPointBefore, - getRange, - moveSelection, - PlateEditor, - setSelection, - TNode, - TText, - Value, - WithPlatePlugin, -} from '@udecode/plate-common'; -import { Range } from 'slate'; - -import { ELEMENT_SLASH_INPUT } from './createSlashPlugin'; -import { - findSlashInput, - isNodeSlashInput, - isSelectionInSlashInput, -} from './queries/index'; -import { removeSlashInput } from './transforms'; -import { SlashPlugin, TSlashInputElement } from './types'; - -export const withSlashCommand = < - V extends Value = Value, - E extends PlateEditor = PlateEditor, ->( - editor: E, - { - options: { id, trigger, triggerPreviousCharPattern, query, inputCreation }, - }: WithPlatePlugin -) => { - const { type } = getPlugin<{}, V>(editor, ELEMENT_SLASH_INPUT); - - const { - apply, - insertBreak, - insertText, - deleteBackward, - insertFragment, - insertTextData, - insertNode, - } = editor; - - editor.insertText = (text) => { - if ( - !editor.selection || - text !== trigger || - (query && !query(editor as PlateEditor)) - ) { - return insertText(text); - } - - // Make sure a slash input is created at the beginning of line or after a whitespace - const previousChar = getEditorString( - editor, - getRange( - editor, - editor.selection, - getPointBefore(editor, editor.selection) - ) - ); - const matchesPreviousCharPattern = - triggerPreviousCharPattern?.test(previousChar); - - if (matchesPreviousCharPattern && text === trigger) { - const data: TSlashInputElement = { - type, - children: [{ text: '' }], - trigger, - }; - if (inputCreation) { - data[inputCreation.key] = inputCreation.value; - } - return insertNode(data); - } - - return insertText(text); - }; - - editor.apply = (operation) => { - apply(operation); - - if (operation.type === 'insert_text' || operation.type === 'remove_text') { - } else if (operation.type === 'set_selection') { - } else if ( - operation.type === 'insert_node' && - isNodeSlashInput(editor, operation.node as TNode) - ) { - // if ((operation.node as TSlashInputElement).trigger !== trigger) { - // return; - // } - - // const text = - // ((operation.node as TSlashInputElement).children as TText[])[0]?.text ?? - // ''; - - // if ( - // inputCreation === undefined || - // operation.node[inputCreation.key] === inputCreation.value - // ) { - // // Needed for undo - after an undo a slash insert we only receive - // // an insert_node with the slash input, i.e. nothing indicating that it - // // was an undo. - // setSelection(editor, { - // anchor: { path: operation.path.concat([0]), offset: text.length }, - // focus: { path: operation.path.concat([0]), offset: text.length }, - // }); - - // comboboxActions.open({ - // activeId: id!, - // text, - // targetRange: editor.selection, - // }); - // } - } else if ( - operation.type === 'remove_node' && - isNodeSlashInput(editor, operation.node as TNode) - ) { - // if ((operation.node as TSlashInputElement).trigger !== trigger) { - // return; - // } - // comboboxActions.reset(); - } - }; - - return editor; -}; From 2f486a2fda347c92abde172b6369c77fc665500a Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Wed, 1 May 2024 18:56:52 +0100 Subject: [PATCH 06/63] Prototype InlineCombobox component --- .../default/example/playground-demo.tsx | 2 - .../default/plate-ui/slash-combobox.tsx | 36 --- .../default/plate-ui/slash-input-element.tsx | 289 ++++++++++++++---- 3 files changed, 222 insertions(+), 105 deletions(-) delete mode 100644 apps/www/src/registry/default/plate-ui/slash-combobox.tsx diff --git a/apps/www/src/registry/default/example/playground-demo.tsx b/apps/www/src/registry/default/example/playground-demo.tsx index f3b3d6d629..c7cd5dbb15 100644 --- a/apps/www/src/registry/default/example/playground-demo.tsx +++ b/apps/www/src/registry/default/example/playground-demo.tsx @@ -438,8 +438,6 @@ export default function PlaygroundDemo({ id }: { id?: ValueId }) { )} - {/**/} - {isEnabled('cursoroverlay', id) && ( )} diff --git a/apps/www/src/registry/default/plate-ui/slash-combobox.tsx b/apps/www/src/registry/default/plate-ui/slash-combobox.tsx deleted file mode 100644 index 200c2adbf6..0000000000 --- a/apps/www/src/registry/default/plate-ui/slash-combobox.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react'; -import { ComboboxProps } from '@udecode/plate-combobox'; -import { getPluginOptions, useEditorRef } from '@udecode/plate-common'; -import { - getSlashOnSelectItem, - KEY_SLASH_COMMAND, - SlashPlugin, -} from '@udecode/plate-slash-command'; - -import { Combobox } from './combobox'; - -export function SlashCombobox({ - pluginKey = KEY_SLASH_COMMAND, - id = pluginKey, - ...props -}: Partial & { - pluginKey?: string; -}) { - const editor = useEditorRef(); - - const { trigger } = getPluginOptions(editor, pluginKey); - - return ( -
e.preventDefault()}> - -
- ); -} diff --git a/apps/www/src/registry/default/plate-ui/slash-input-element.tsx b/apps/www/src/registry/default/plate-ui/slash-input-element.tsx index 8b07cae93c..44ceaead50 100644 --- a/apps/www/src/registry/default/plate-ui/slash-input-element.tsx +++ b/apps/www/src/registry/default/plate-ui/slash-input-element.tsx @@ -1,43 +1,224 @@ -import React, { useState } from 'react'; +import React, { ReactNode, startTransition, useMemo, useState } from 'react'; import { Combobox, ComboboxItem, ComboboxPopover, ComboboxProvider, } from '@ariakit/react'; -import { withRef } from '@udecode/cn'; +import { cn, withRef } from '@udecode/cn'; import { useComboboxInput, useHTMLInputCursorState, } from '@udecode/plate-combobox'; -import { insertText, moveSelection, PlateElement } from '@udecode/plate-common'; +import { + insertText, + moveSelection, + PlateElement, + useEditorRef, +} from '@udecode/plate-common'; +import { + ELEMENT_H1, + ELEMENT_H2, + ELEMENT_H3, + ELEMENT_H4, + ELEMENT_H5, + ELEMENT_H6, +} from '@udecode/plate-heading'; + +type InlineComboboxItem = { + value: string; + label: string; + aliases?: string[]; + onSelect?: () => void; +}; + +// Smart filter by words, using prefix matching on last word only +export const filterPredicate = (haystack: string, needle: string): boolean => { + const haystackWords = haystack.trim().split(/\s+/); + const needleWords = needle.trim().split(/\s+/); + + return needleWords.every((needleWord, i) => { + const allowPrefix = i === needleWords.length - 1; + + return haystackWords.some((unslicedHaystackWord) => { + const haystackWord = allowPrefix + ? unslicedHaystackWord.slice(0, needleWord.length) + : unslicedHaystackWord; + + return ( + haystackWord.localeCompare(needleWord, undefined, { + usage: 'search', + sensitivity: 'base', + }) === 0 + ); + }); + }); +}; + +const comboboxItemClassName = + 'relative flex h-9 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none'; +const comboboxItemInteractiveClassName = + 'cursor-pointer transition-colors hover:bg-accent hover:text-accent-foreground data-[active-item=true]:bg-accent data-[active-item=true]:text-accent-foreground'; + +interface InlineComboboxProps { + trigger: string; + items: TItem[]; + renderItem?: (item: TItem) => ReactNode; + renderEmpty?: ReactNode; + onSelectItem?: (item: TItem) => void; +} + +const InlineCombobox = ({ + trigger, + items, + renderItem, + renderEmpty, + onSelectItem, +}: InlineComboboxProps) => { + const editor = useEditorRef(); + const [value, setValue] = useState(''); + const inputRef = React.useRef(null); + const cursorState = useHTMLInputCursorState(inputRef); + + const filteredItems = useMemo( + () => + items.filter(({ label, aliases = [] }) => + [label, ...aliases].some((alias) => filterPredicate(alias, value)) + ), + [items, value] + ); + + const { removeInput, props: inputProps } = useComboboxInput({ + ref: inputRef, + cursorState, + onCancelInput: (cause) => { + if (cause !== 'backspace') { + insertText(editor, trigger + value); + } + + if (cause === 'arrowLeft' || cause === 'arrowRight') { + moveSelection(editor, { + distance: 1, + reverse: cause === 'arrowLeft', + }); + } + }, + }); + + /** + * To create an auto-resizing input, we render a visually hidden span + * containing the input value and position the input element on top of it. + * This works well for all cases except when input exceeds the width of the + * container. + */ + + return ( + + {trigger} + + 0 || renderEmpty !== undefined} + setValue={(newValue) => startTransition(() => setValue(newValue))} + > + + + + + + + + {filteredItems.map((item) => ( + { + removeInput(true); + item.onSelect?.(); + onSelectItem?.(item); + }} + > + {renderItem ? renderItem(item) : item.label} + + ))} + + {filteredItems.length === 0 && renderEmpty && ( +
{renderEmpty}
+ )} +
+
+
+ ); +}; + +type SlashCommandRule = InlineComboboxItem & { + icon: ReactNode; +}; + +const rules: SlashCommandRule[] = [ + { + value: 'apple', + label: 'Apple', + icon: '🍎', + }, + { + value: 'banana', + label: 'Banana', + icon: '🍌', + }, + { + value: 'cherry', + label: 'Cherry', + icon: '🍒', + }, + { + value: ELEMENT_H1, + label: 'Heading 1', + icon: 'H1', + }, + { + value: ELEMENT_H2, + label: 'Heading 2', + icon: 'H2', + }, + { + value: ELEMENT_H3, + label: 'Heading 3', + icon: 'H3', + }, + { + value: ELEMENT_H4, + label: 'Heading 4', + icon: 'H4', + }, + { + value: ELEMENT_H5, + label: 'Heading 5', + icon: 'H5', + }, + { + value: ELEMENT_H6, + label: 'Heading 6', + icon: 'H6', + }, +]; export const SlashInputElement = withRef( ({ className, ...props }, ref) => { const { children, element, editor } = props; - const inputRef = React.useRef(null); - - const cursorState = useHTMLInputCursorState(inputRef); - - const [value, setValue] = useState(''); - - const { removeInput, props: inputProps } = useComboboxInput({ - ref: inputRef, - cursorState, - cancelInputOnBlur: false, - onCancelInput: (cause) => { - if (cause !== 'backspace') { - insertText(editor, '/' + value); - } - - if (cause === 'arrowLeft' || cause === 'arrowRight') { - moveSelection(editor, { - distance: 1, - reverse: cause === 'arrowLeft', - }); - } - }, - }); return ( ( data-slate-value={element.value} {...props} > - - / - { - removeInput(true); - insertText(editor, 'You selected: ' + selectedValue); - }} - > - - {/** - To create an auto-resizing input, we render a visually hidden span - containing the input value and position the input element on top of - it. This works well for all cases except when input exceeds the - width of the container. - */} - - - - - - - Apple - - Banana - - Cherry - - - + ( + <> + {icon} + {label} + + )} + renderEmpty="No matching commands found" + onSelectItem={({ label }) => { + insertText(editor, 'Selected ' + label); + }} + /> + {children} ); From 7ed9cdbd696c48bb56501bcbabcf4424a9e2777a Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Wed, 1 May 2024 19:15:51 +0100 Subject: [PATCH 07/63] Refactor out InlineCombobox component --- .../default/plate-ui/inline-combobox.tsx | 128 ++++++++++++++ .../default/plate-ui/slash-input-element.tsx | 163 +----------------- packages/combobox/src/index.ts | 1 + packages/combobox/src/types.ts | 9 +- packages/combobox/src/utils/index.ts | 5 + packages/combobox/src/utils/matchWords.ts | 47 +++++ .../src/withInsertTextTriggerCombobox.ts | 3 +- 7 files changed, 197 insertions(+), 159 deletions(-) create mode 100644 apps/www/src/registry/default/plate-ui/inline-combobox.tsx create mode 100644 packages/combobox/src/utils/index.ts create mode 100644 packages/combobox/src/utils/matchWords.ts diff --git a/apps/www/src/registry/default/plate-ui/inline-combobox.tsx b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx new file mode 100644 index 0000000000..cc7c8ad3c5 --- /dev/null +++ b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx @@ -0,0 +1,128 @@ +import React, { ReactNode, startTransition, useMemo, useState } from 'react'; +import { + Combobox, + ComboboxItem, + ComboboxPopover, + ComboboxProvider, +} from '@ariakit/react'; +import { cn } from '@udecode/cn'; +import { + BaseComboboxItem, + matchWords, + useComboboxInput, + useHTMLInputCursorState, +} from '@udecode/plate-combobox'; +import { insertText, moveSelection, useEditorRef } from '@udecode/plate-common'; + +const comboboxItemClassName = + 'relative flex h-9 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none'; +const comboboxItemInteractiveClassName = + 'cursor-pointer transition-colors hover:bg-accent hover:text-accent-foreground data-[active-item=true]:bg-accent data-[active-item=true]:text-accent-foreground'; + +const defaultMatchItem = ( + { label, aliases = [] }: BaseComboboxItem, + query: string +) => [label, ...aliases].some((alias) => matchWords(alias, query)); + +interface InlineComboboxProps { + trigger: string; + items: TItem[]; + matchItem?: (item: TItem, query: string) => boolean; + renderItem?: (item: TItem) => ReactNode; + renderEmpty?: ReactNode; + onSelectItem?: (item: TItem) => void; +} + +export const InlineCombobox = ({ + trigger, + items, + matchItem = defaultMatchItem, + renderItem = ({ label }) => label, + renderEmpty, + onSelectItem, +}: InlineComboboxProps) => { + const editor = useEditorRef(); + const [value, setValue] = useState(''); + const inputRef = React.useRef(null); + const cursorState = useHTMLInputCursorState(inputRef); + + const filteredItems = useMemo( + () => items.filter((item) => matchItem(item, value)), + [items, matchItem, value] + ); + + const { removeInput, props: inputProps } = useComboboxInput({ + ref: inputRef, + cursorState, + onCancelInput: (cause) => { + if (cause !== 'backspace') { + insertText(editor, trigger + value); + } + + if (cause === 'arrowLeft' || cause === 'arrowRight') { + moveSelection(editor, { + distance: 1, + reverse: cause === 'arrowLeft', + }); + } + }, + }); + + /** + * To create an auto-resizing input, we render a visually hidden span + * containing the input value and position the input element on top of it. + * This works well for all cases except when input exceeds the width of the + * container. + */ + + return ( + + {trigger} + + 0 || renderEmpty !== undefined} + setValue={(newValue) => startTransition(() => setValue(newValue))} + > + + + + + + + + {filteredItems.map((item) => ( + { + removeInput(true); + item.onSelect?.(); + onSelectItem?.(item); + }} + > + {renderItem(item)} + + ))} + + {filteredItems.length === 0 && renderEmpty && ( +
{renderEmpty}
+ )} +
+
+
+ ); +}; diff --git a/apps/www/src/registry/default/plate-ui/slash-input-element.tsx b/apps/www/src/registry/default/plate-ui/slash-input-element.tsx index 44ceaead50..6cf2988407 100644 --- a/apps/www/src/registry/default/plate-ui/slash-input-element.tsx +++ b/apps/www/src/registry/default/plate-ui/slash-input-element.tsx @@ -1,21 +1,7 @@ -import React, { ReactNode, startTransition, useMemo, useState } from 'react'; -import { - Combobox, - ComboboxItem, - ComboboxPopover, - ComboboxProvider, -} from '@ariakit/react'; -import { cn, withRef } from '@udecode/cn'; -import { - useComboboxInput, - useHTMLInputCursorState, -} from '@udecode/plate-combobox'; -import { - insertText, - moveSelection, - PlateElement, - useEditorRef, -} from '@udecode/plate-common'; +import React, { ReactNode } from 'react'; +import { withRef } from '@udecode/cn'; +import { BaseComboboxItem } from '@udecode/plate-combobox'; +import { insertText, PlateElement } from '@udecode/plate-common'; import { ELEMENT_H1, ELEMENT_H2, @@ -25,146 +11,9 @@ import { ELEMENT_H6, } from '@udecode/plate-heading'; -type InlineComboboxItem = { - value: string; - label: string; - aliases?: string[]; - onSelect?: () => void; -}; - -// Smart filter by words, using prefix matching on last word only -export const filterPredicate = (haystack: string, needle: string): boolean => { - const haystackWords = haystack.trim().split(/\s+/); - const needleWords = needle.trim().split(/\s+/); - - return needleWords.every((needleWord, i) => { - const allowPrefix = i === needleWords.length - 1; - - return haystackWords.some((unslicedHaystackWord) => { - const haystackWord = allowPrefix - ? unslicedHaystackWord.slice(0, needleWord.length) - : unslicedHaystackWord; - - return ( - haystackWord.localeCompare(needleWord, undefined, { - usage: 'search', - sensitivity: 'base', - }) === 0 - ); - }); - }); -}; - -const comboboxItemClassName = - 'relative flex h-9 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none'; -const comboboxItemInteractiveClassName = - 'cursor-pointer transition-colors hover:bg-accent hover:text-accent-foreground data-[active-item=true]:bg-accent data-[active-item=true]:text-accent-foreground'; - -interface InlineComboboxProps { - trigger: string; - items: TItem[]; - renderItem?: (item: TItem) => ReactNode; - renderEmpty?: ReactNode; - onSelectItem?: (item: TItem) => void; -} - -const InlineCombobox = ({ - trigger, - items, - renderItem, - renderEmpty, - onSelectItem, -}: InlineComboboxProps) => { - const editor = useEditorRef(); - const [value, setValue] = useState(''); - const inputRef = React.useRef(null); - const cursorState = useHTMLInputCursorState(inputRef); - - const filteredItems = useMemo( - () => - items.filter(({ label, aliases = [] }) => - [label, ...aliases].some((alias) => filterPredicate(alias, value)) - ), - [items, value] - ); - - const { removeInput, props: inputProps } = useComboboxInput({ - ref: inputRef, - cursorState, - onCancelInput: (cause) => { - if (cause !== 'backspace') { - insertText(editor, trigger + value); - } - - if (cause === 'arrowLeft' || cause === 'arrowRight') { - moveSelection(editor, { - distance: 1, - reverse: cause === 'arrowLeft', - }); - } - }, - }); - - /** - * To create an auto-resizing input, we render a visually hidden span - * containing the input value and position the input element on top of it. - * This works well for all cases except when input exceeds the width of the - * container. - */ - - return ( - - {trigger} - - 0 || renderEmpty !== undefined} - setValue={(newValue) => startTransition(() => setValue(newValue))} - > - - - - - - - - {filteredItems.map((item) => ( - { - removeInput(true); - item.onSelect?.(); - onSelectItem?.(item); - }} - > - {renderItem ? renderItem(item) : item.label} - - ))} - - {filteredItems.length === 0 && renderEmpty && ( -
{renderEmpty}
- )} -
-
-
- ); -}; +import { InlineCombobox } from './inline-combobox'; -type SlashCommandRule = InlineComboboxItem & { +type SlashCommandRule = BaseComboboxItem & { icon: ReactNode; }; diff --git a/packages/combobox/src/index.ts b/packages/combobox/src/index.ts index 74fb607813..479d58af77 100644 --- a/packages/combobox/src/index.ts +++ b/packages/combobox/src/index.ts @@ -6,3 +6,4 @@ export * from './types'; export * from './withInsertTextTriggerCombobox'; export * from './hooks/index'; export * from './legacy-combobox-delete-me/index'; +export * from './utils/index'; diff --git a/packages/combobox/src/types.ts b/packages/combobox/src/types.ts index 7c4bdb2432..58a4999f41 100644 --- a/packages/combobox/src/types.ts +++ b/packages/combobox/src/types.ts @@ -1,4 +1,4 @@ -import {PlateEditor, TElement} from "@udecode/plate-common"; +import { PlateEditor, TElement } from '@udecode/plate-common'; export interface TriggerComboboxPlugin { combobox?: { @@ -22,3 +22,10 @@ export type CancelComboboxInputCause = | 'arrowRight' | 'deselect' | 'blur'; + +export type BaseComboboxItem = { + value: string; + label: string; + aliases?: string[]; + onSelect?: () => void; +}; diff --git a/packages/combobox/src/utils/index.ts b/packages/combobox/src/utils/index.ts new file mode 100644 index 0000000000..09edaea9dc --- /dev/null +++ b/packages/combobox/src/utils/index.ts @@ -0,0 +1,5 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './matchWords'; diff --git a/packages/combobox/src/utils/matchWords.ts b/packages/combobox/src/utils/matchWords.ts new file mode 100644 index 0000000000..e9be6c9c96 --- /dev/null +++ b/packages/combobox/src/utils/matchWords.ts @@ -0,0 +1,47 @@ +export interface MatchWordsOptions { + prefixMode?: 'none' | 'all-words' | 'last-word'; + wordQuantifier?: 'match-all' | 'match-any'; +} + +export const matchWords = ( + haystack: string, + needle: string, + { + prefixMode = 'last-word', + wordQuantifier = 'match-all', + }: MatchWordsOptions = {} +): boolean => { + const haystackWords = haystack.trim().split(/\s+/); + const needleWords = needle.trim().split(/\s+/); + + const quantifier = wordQuantifier === 'match-all' ? 'every' : 'some'; + + return needleWords[quantifier]((needleWord, i) => { + const allowPrefix = (() => { + switch (prefixMode) { + case 'none': { + return false; + } + case 'all-words': { + return true; + } + case 'last-word': { + return i === needleWords.length - 1; + } + } + })(); + + return haystackWords.some((unslicedHaystackWord) => { + const haystackWord = allowPrefix + ? unslicedHaystackWord.slice(0, needleWord.length) + : unslicedHaystackWord; + + return ( + haystackWord.localeCompare(needleWord, undefined, { + usage: 'search', + sensitivity: 'base', + }) === 0 + ); + }); + }); +}; diff --git a/packages/combobox/src/withInsertTextTriggerCombobox.ts b/packages/combobox/src/withInsertTextTriggerCombobox.ts index 2e4b4abf26..736cd21b30 100644 --- a/packages/combobox/src/withInsertTextTriggerCombobox.ts +++ b/packages/combobox/src/withInsertTextTriggerCombobox.ts @@ -7,7 +7,8 @@ import { Value, WithPlatePlugin, } from '@udecode/plate-common'; -import {TriggerComboboxPlugin} from './types'; + +import { TriggerComboboxPlugin } from './types'; export const withInsertTextTriggerCombobox = < V extends Value = Value, From 1783605cf67926131445cb0c2359d2c4799a8987 Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Wed, 1 May 2024 19:23:49 +0100 Subject: [PATCH 08/63] Add tests for matchWords --- .../combobox/src/utils/matchWords.spec.ts | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 packages/combobox/src/utils/matchWords.spec.ts diff --git a/packages/combobox/src/utils/matchWords.spec.ts b/packages/combobox/src/utils/matchWords.spec.ts new file mode 100644 index 0000000000..8376a0cf56 --- /dev/null +++ b/packages/combobox/src/utils/matchWords.spec.ts @@ -0,0 +1,71 @@ +import { matchWords, MatchWordsOptions } from './matchWords'; + +describe('matchWords', () => { + describe('with default options', () => { + describe('single word', () => { + it('matches simple prefix', () => { + expect(matchWords('hello', 'he')).toBe(true); + }); + + it('does not match non-prefix', () => { + expect(matchWords('hello', 'lo')).toBe(false); + }); + + it('is case-insensitive', () => { + expect(matchWords('hello', 'HE')).toBe(true); + }); + + it('is diacritic-insensitive', () => { + expect(matchWords('hello', 'hé')).toBe(true); + }); + }); + + describe('multiple words', () => { + it('matches when all words in query match', () => { + expect(matchWords('hello world', 'world hello')).toBe(true); + expect(matchWords('hello world', 'world')).toBe(true); + }); + + it('does not match when not all words in query match', () => { + expect(matchWords('hello world', 'hello other')).toBe(false); + }); + + it('allows prefix for last word', () => { + expect(matchWords('hello world', 'world he')).toBe(true); + }); + + it('does not allow prefix for non-last word', () => { + expect(matchWords('hello world', 'wor hello')).toBe(false); + }); + }); + }); + + describe('with prefix mode disabled', () => { + const options: MatchWordsOptions = { prefixMode: 'none' }; + + it('only matches whole words', () => { + expect(matchWords('hello world', 'wor', options)).toBe(false); + expect(matchWords('hello world', 'world', options)).toBe(true); + }); + }); + + describe('with prefix mode set to all words', () => { + const options: MatchWordsOptions = { prefixMode: 'all-words' }; + + it('allows prefix for all words', () => { + expect(matchWords('hello world', 'wor hel', options)).toBe(true); + }); + }); + + describe('with word quantifier set to match any', () => { + const options: MatchWordsOptions = { wordQuantifier: 'match-any' }; + + it('matches when any word in query matches', () => { + expect(matchWords('hello world', 'other hello', options)).toBe(true); + }); + + it('does not match when no word in query matches', () => { + expect(matchWords('hello world', 'other other', options)).toBe(false); + }); + }); +}); From f86618316fbd4b5cdfe54a73d635300e05714808 Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Wed, 1 May 2024 19:45:11 +0100 Subject: [PATCH 09/63] Fully implement slash commands using new combobox --- .../src/lib/plate/demo/values/slashRules.ts | 52 ----------- .../default/example/playground-demo.tsx | 7 +- .../default/plate-ui/inline-combobox.tsx | 10 +-- .../default/plate-ui/slash-input-element.tsx | 90 +++++++++---------- packages/combobox/src/types.ts | 6 +- packages/slash-command/src/types.ts | 12 +-- 6 files changed, 54 insertions(+), 123 deletions(-) delete mode 100644 apps/www/src/lib/plate/demo/values/slashRules.ts diff --git a/apps/www/src/lib/plate/demo/values/slashRules.ts b/apps/www/src/lib/plate/demo/values/slashRules.ts deleted file mode 100644 index 89bbd220b3..0000000000 --- a/apps/www/src/lib/plate/demo/values/slashRules.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { PlateEditor, toggleNodeType } from '@udecode/plate-core'; -import { ELEMENT_H1, ELEMENT_H2, ELEMENT_H3 } from '@udecode/plate-heading'; -import { ListStyleType, toggleIndentList } from '@udecode/plate-indent-list'; -import { SlashRule } from '@udecode/plate-slash-command'; -import { focusEditor } from '@udecode/slate-react'; - -export const SLASH_RULES: SlashRule[] = [ - { - key: ELEMENT_H1, - text: 'Heading 1', - onTrigger(editor: PlateEditor) { - toggleNodeType(editor, { activeType: ELEMENT_H1 }); - focusEditor(editor); - }, - }, - { - key: ELEMENT_H2, - text: 'Heading 2', - onTrigger(editor: PlateEditor) { - toggleNodeType(editor, { activeType: ELEMENT_H2 }); - focusEditor(editor); - }, - }, - { - key: ELEMENT_H3, - text: 'Heading 3', - onTrigger(editor: PlateEditor) { - toggleNodeType(editor, { activeType: ELEMENT_H3 }); - focusEditor(editor); - }, - }, - { - key: ListStyleType.Disc, - text: 'Bulleted list', - onTrigger(editor: PlateEditor) { - toggleIndentList(editor, { - listStyleType: ListStyleType.Disc, - }); - focusEditor(editor); - }, - }, - { - key: ListStyleType.Decimal, - text: 'Numbered list', - onTrigger(editor: PlateEditor) { - toggleIndentList(editor, { - listStyleType: ListStyleType.Decimal, - }); - focusEditor(editor); - }, - }, -]; diff --git a/apps/www/src/registry/default/example/playground-demo.tsx b/apps/www/src/registry/default/example/playground-demo.tsx index c7cd5dbb15..b7f2e83cb5 100644 --- a/apps/www/src/registry/default/example/playground-demo.tsx +++ b/apps/www/src/registry/default/example/playground-demo.tsx @@ -112,7 +112,6 @@ import { HTML5Backend } from 'react-dnd-html5-backend'; import { ValueId } from '@/config/customizer-plugins'; import { captionPlugin } from '@/lib/plate/demo/plugins/captionPlugin'; -import { SLASH_RULES } from '@/lib/plate/demo/values/slashRules'; import { settingsStore } from '@/components/context/settings-store'; import { PlaygroundFixedToolbarButtons } from '@/components/plate-ui/playground-fixed-toolbar-buttons'; import { PlaygroundFloatingToolbarButtons } from '@/components/plate-ui/playground-floating-toolbar-buttons'; @@ -178,11 +177,7 @@ export const usePlaygroundPlugins = ({ triggerPreviousCharPattern: /^$|^[\s"']$/, }, }), - createSlashPlugin({ - options: { - rules: SLASH_RULES, - }, - }), + createSlashPlugin(), createTablePlugin({ enabled: !!enabled.table, options: { diff --git a/apps/www/src/registry/default/plate-ui/inline-combobox.tsx b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx index cc7c8ad3c5..3f73991b23 100644 --- a/apps/www/src/registry/default/plate-ui/inline-combobox.tsx +++ b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx @@ -7,7 +7,7 @@ import { } from '@ariakit/react'; import { cn } from '@udecode/cn'; import { - BaseComboboxItem, + BaseComboboxItemWithEditor, matchWords, useComboboxInput, useHTMLInputCursorState, @@ -20,11 +20,11 @@ const comboboxItemInteractiveClassName = 'cursor-pointer transition-colors hover:bg-accent hover:text-accent-foreground data-[active-item=true]:bg-accent data-[active-item=true]:text-accent-foreground'; const defaultMatchItem = ( - { label, aliases = [] }: BaseComboboxItem, + { label, aliases = [] }: BaseComboboxItemWithEditor, query: string ) => [label, ...aliases].some((alias) => matchWords(alias, query)); -interface InlineComboboxProps { +interface InlineComboboxProps { trigger: string; items: TItem[]; matchItem?: (item: TItem, query: string) => boolean; @@ -33,7 +33,7 @@ interface InlineComboboxProps { onSelectItem?: (item: TItem) => void; } -export const InlineCombobox = ({ +export const InlineCombobox = ({ trigger, items, matchItem = defaultMatchItem, @@ -110,7 +110,7 @@ export const InlineCombobox = ({ )} onClick={() => { removeInput(true); - item.onSelect?.(); + item.onSelect?.(editor); onSelectItem?.(item); }} > diff --git a/apps/www/src/registry/default/plate-ui/slash-input-element.tsx b/apps/www/src/registry/default/plate-ui/slash-input-element.tsx index 6cf2988407..bd00d685a0 100644 --- a/apps/www/src/registry/default/plate-ui/slash-input-element.tsx +++ b/apps/www/src/registry/default/plate-ui/slash-input-element.tsx @@ -1,73 +1,70 @@ -import React, { ReactNode } from 'react'; +import React, { ComponentType, SVGProps } from 'react'; import { withRef } from '@udecode/cn'; -import { BaseComboboxItem } from '@udecode/plate-combobox'; -import { insertText, PlateElement } from '@udecode/plate-common'; -import { - ELEMENT_H1, - ELEMENT_H2, - ELEMENT_H3, - ELEMENT_H4, - ELEMENT_H5, - ELEMENT_H6, -} from '@udecode/plate-heading'; +import { BaseComboboxItemWithEditor } from '@udecode/plate-combobox'; +import { PlateElement, toggleNodeType } from '@udecode/plate-common'; +import { ELEMENT_H1, ELEMENT_H2, ELEMENT_H3 } from '@udecode/plate-heading'; +import { ListStyleType, toggleIndentList } from '@udecode/plate-indent-list'; + +import { Icons } from '@/components/icons'; import { InlineCombobox } from './inline-combobox'; -type SlashCommandRule = BaseComboboxItem & { - icon: ReactNode; +export type SlashCommandRule = BaseComboboxItemWithEditor & { + icon: ComponentType>; }; const rules: SlashCommandRule[] = [ - { - value: 'apple', - label: 'Apple', - icon: '🍎', - }, - { - value: 'banana', - label: 'Banana', - icon: '🍌', - }, - { - value: 'cherry', - label: 'Cherry', - icon: '🍒', - }, { value: ELEMENT_H1, label: 'Heading 1', - icon: 'H1', + icon: Icons.h1, + onSelect: (editor) => { + toggleNodeType(editor, { activeType: ELEMENT_H1 }); + }, }, { value: ELEMENT_H2, label: 'Heading 2', - icon: 'H2', + icon: Icons.h2, + onSelect: (editor) => { + toggleNodeType(editor, { activeType: ELEMENT_H2 }); + }, }, { value: ELEMENT_H3, label: 'Heading 3', - icon: 'H3', - }, - { - value: ELEMENT_H4, - label: 'Heading 4', - icon: 'H4', + icon: Icons.h3, + onSelect: (editor) => { + toggleNodeType(editor, { activeType: ELEMENT_H3 }); + }, }, { - value: ELEMENT_H5, - label: 'Heading 5', - icon: 'H5', + value: ListStyleType.Disc, + label: 'Bulleted list', + icon: Icons.ul, + aliases: ['ul', 'ordered list'], + onSelect: (editor) => { + toggleIndentList(editor, { + listStyleType: ListStyleType.Disc, + }); + }, }, { - value: ELEMENT_H6, - label: 'Heading 6', - icon: 'H6', + value: ListStyleType.Decimal, + label: 'Numbered list', + icon: Icons.ol, + aliases: ['ol', 'unordered list'], + onSelect: (editor) => { + toggleIndentList(editor, { + listStyleType: ListStyleType.Decimal, + }); + }, }, ]; export const SlashInputElement = withRef( ({ className, ...props }, ref) => { - const { children, element, editor } = props; + const { children, element } = props; return ( ( ( + renderItem={({ icon: Icon, label }) => ( <> - {icon} + {label} )} renderEmpty="No matching commands found" - onSelectItem={({ label }) => { - insertText(editor, 'Selected ' + label); - }} /> {children} diff --git a/packages/combobox/src/types.ts b/packages/combobox/src/types.ts index 58a4999f41..2e5b74c94e 100644 --- a/packages/combobox/src/types.ts +++ b/packages/combobox/src/types.ts @@ -23,9 +23,11 @@ export type CancelComboboxInputCause = | 'deselect' | 'blur'; -export type BaseComboboxItem = { +export type BaseComboboxItem = { value: string; label: string; aliases?: string[]; - onSelect?: () => void; + onSelect?: (...args: OnSelectArgs) => void; }; + +export type BaseComboboxItemWithEditor = BaseComboboxItem<[PlateEditor]>; diff --git a/packages/slash-command/src/types.ts b/packages/slash-command/src/types.ts index f08d4a93ce..69e3819263 100644 --- a/packages/slash-command/src/types.ts +++ b/packages/slash-command/src/types.ts @@ -1,14 +1,6 @@ import { TriggerComboboxPlugin } from '@udecode/plate-combobox'; -import { PlateEditor, TElement } from '@udecode/plate-common'; +import { TElement } from '@udecode/plate-common'; export interface TSlashInputElement extends TElement {} -export interface SlashRule { - key: string; - text: React.ReactNode; - onTrigger: (editor: PlateEditor, key: string) => void; -} - -export interface SlashPlugin extends TriggerComboboxPlugin { - rules?: SlashRule[]; -} +export interface SlashPlugin extends TriggerComboboxPlugin {} From 2c9858732537b7b3c6c33f97e9b9074d32620a54 Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Wed, 1 May 2024 19:48:54 +0100 Subject: [PATCH 10/63] Fix error when focusing --- packages/combobox/src/hooks/useComboboxInput.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/combobox/src/hooks/useComboboxInput.ts b/packages/combobox/src/hooks/useComboboxInput.ts index 0183f6d7ad..7d69b26c8d 100644 --- a/packages/combobox/src/hooks/useComboboxInput.ts +++ b/packages/combobox/src/hooks/useComboboxInput.ts @@ -57,7 +57,7 @@ export const useComboboxInput = ({ removeNodes(editor, { at: path }); if (shouldFocusEditor) { - setTimeout(() => focusEditor(editor)); + focusEditor(editor); } }, [editor, element] From 297657e232dd46068086dac4a999ba6cd5bb3f17 Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Wed, 1 May 2024 19:49:05 +0100 Subject: [PATCH 11/63] Fix CSS leaking into combobox popover --- .../default/plate-ui/inline-combobox.tsx | 46 ++++++++++--------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/apps/www/src/registry/default/plate-ui/inline-combobox.tsx b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx index 3f73991b23..b249f4c5ce 100644 --- a/apps/www/src/registry/default/plate-ui/inline-combobox.tsx +++ b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx @@ -4,6 +4,7 @@ import { ComboboxItem, ComboboxPopover, ComboboxProvider, + Portal, } from '@ariakit/react'; import { cn } from '@udecode/cn'; import { @@ -100,28 +101,31 @@ export const InlineCombobox = ({ /> - - {filteredItems.map((item) => ( - { - removeInput(true); - item.onSelect?.(editor); - onSelectItem?.(item); - }} - > - {renderItem(item)} - - ))} + {/* Portal prevents CSS from leaking into popover */} + + + {filteredItems.map((item) => ( + { + removeInput(true); + item.onSelect?.(editor); + onSelectItem?.(item); + }} + > + {renderItem(item)} + + ))} - {filteredItems.length === 0 && renderEmpty && ( -
{renderEmpty}
- )} -
+ {filteredItems.length === 0 && renderEmpty && ( +
{renderEmpty}
+ )} +
+ ); From f9f21b6c31c2ec914be5f23034418eeab62a5ce4 Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Wed, 1 May 2024 19:53:44 +0100 Subject: [PATCH 12/63] Fix: Aliases backwards for [un]ordered lists --- .../www/src/registry/default/plate-ui/slash-input-element.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/www/src/registry/default/plate-ui/slash-input-element.tsx b/apps/www/src/registry/default/plate-ui/slash-input-element.tsx index bd00d685a0..25d4efaa1a 100644 --- a/apps/www/src/registry/default/plate-ui/slash-input-element.tsx +++ b/apps/www/src/registry/default/plate-ui/slash-input-element.tsx @@ -42,7 +42,7 @@ const rules: SlashCommandRule[] = [ value: ListStyleType.Disc, label: 'Bulleted list', icon: Icons.ul, - aliases: ['ul', 'ordered list'], + aliases: ['ul', 'unordered list'], onSelect: (editor) => { toggleIndentList(editor, { listStyleType: ListStyleType.Disc, @@ -53,7 +53,7 @@ const rules: SlashCommandRule[] = [ value: ListStyleType.Decimal, label: 'Numbered list', icon: Icons.ol, - aliases: ['ol', 'unordered list'], + aliases: ['ol', 'ordered list'], onSelect: (editor) => { toggleIndentList(editor, { listStyleType: ListStyleType.Decimal, From 2890a67e391a7ec9d7dabbb14b967aa43ec1ed38 Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Thu, 2 May 2024 08:35:47 +0100 Subject: [PATCH 13/63] Use cva for combobox item styles --- .../default/plate-ui/inline-combobox.tsx | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/apps/www/src/registry/default/plate-ui/inline-combobox.tsx b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx index b249f4c5ce..99062612bf 100644 --- a/apps/www/src/registry/default/plate-ui/inline-combobox.tsx +++ b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx @@ -6,7 +6,6 @@ import { ComboboxProvider, Portal, } from '@ariakit/react'; -import { cn } from '@udecode/cn'; import { BaseComboboxItemWithEditor, matchWords, @@ -14,11 +13,22 @@ import { useHTMLInputCursorState, } from '@udecode/plate-combobox'; import { insertText, moveSelection, useEditorRef } from '@udecode/plate-common'; +import { cva } from 'class-variance-authority'; -const comboboxItemClassName = - 'relative flex h-9 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none'; -const comboboxItemInteractiveClassName = - 'cursor-pointer transition-colors hover:bg-accent hover:text-accent-foreground data-[active-item=true]:bg-accent data-[active-item=true]:text-accent-foreground'; +const comboboxItemVariants = cva( + 'relative flex h-9 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none', + { + variants: { + interactive: { + true: 'cursor-pointer transition-colors hover:bg-accent hover:text-accent-foreground data-[active-item=true]:bg-accent data-[active-item=true]:text-accent-foreground', + false: '', + }, + }, + defaultVariants: { + interactive: true, + }, + } +); const defaultMatchItem = ( { label, aliases = [] }: BaseComboboxItemWithEditor, @@ -107,10 +117,7 @@ export const InlineCombobox = ({ {filteredItems.map((item) => ( { removeInput(true); item.onSelect?.(editor); @@ -122,7 +129,9 @@ export const InlineCombobox = ({ ))} {filteredItems.length === 0 && renderEmpty && ( -
{renderEmpty}
+
+ {renderEmpty} +
)} From b6e1dab7f2ef8e86a76544bcc130dc7894911ab6 Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Thu, 2 May 2024 08:37:47 +0100 Subject: [PATCH 14/63] Refactor aliases -> keywords --- apps/www/src/registry/default/plate-ui/inline-combobox.tsx | 4 ++-- .../www/src/registry/default/plate-ui/slash-input-element.tsx | 4 ++-- packages/combobox/src/types.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/www/src/registry/default/plate-ui/inline-combobox.tsx b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx index 99062612bf..3b25e48562 100644 --- a/apps/www/src/registry/default/plate-ui/inline-combobox.tsx +++ b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx @@ -31,9 +31,9 @@ const comboboxItemVariants = cva( ); const defaultMatchItem = ( - { label, aliases = [] }: BaseComboboxItemWithEditor, + { label, keywords = [] }: BaseComboboxItemWithEditor, query: string -) => [label, ...aliases].some((alias) => matchWords(alias, query)); +) => [label, ...keywords].some((keyword) => matchWords(keyword, query)); interface InlineComboboxProps { trigger: string; diff --git a/apps/www/src/registry/default/plate-ui/slash-input-element.tsx b/apps/www/src/registry/default/plate-ui/slash-input-element.tsx index 25d4efaa1a..1db3d44a95 100644 --- a/apps/www/src/registry/default/plate-ui/slash-input-element.tsx +++ b/apps/www/src/registry/default/plate-ui/slash-input-element.tsx @@ -42,7 +42,7 @@ const rules: SlashCommandRule[] = [ value: ListStyleType.Disc, label: 'Bulleted list', icon: Icons.ul, - aliases: ['ul', 'unordered list'], + keywords: ['ul', 'unordered list'], onSelect: (editor) => { toggleIndentList(editor, { listStyleType: ListStyleType.Disc, @@ -53,7 +53,7 @@ const rules: SlashCommandRule[] = [ value: ListStyleType.Decimal, label: 'Numbered list', icon: Icons.ol, - aliases: ['ol', 'ordered list'], + keywords: ['ol', 'ordered list'], onSelect: (editor) => { toggleIndentList(editor, { listStyleType: ListStyleType.Decimal, diff --git a/packages/combobox/src/types.ts b/packages/combobox/src/types.ts index 2e5b74c94e..90e085ec22 100644 --- a/packages/combobox/src/types.ts +++ b/packages/combobox/src/types.ts @@ -26,7 +26,7 @@ export type CancelComboboxInputCause = export type BaseComboboxItem = { value: string; label: string; - aliases?: string[]; + keywords?: string[]; onSelect?: (...args: OnSelectArgs) => void; }; From 4818f2de7b6683f39acf1f55e5b0513053a88296 Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Thu, 2 May 2024 08:39:18 +0100 Subject: [PATCH 15/63] Remove SlashPlugin type --- packages/slash-command/src/createSlashPlugin.ts | 9 +++++---- packages/slash-command/src/types.ts | 3 --- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/slash-command/src/createSlashPlugin.ts b/packages/slash-command/src/createSlashPlugin.ts index ebf7c2d06a..c0ba40478d 100644 --- a/packages/slash-command/src/createSlashPlugin.ts +++ b/packages/slash-command/src/createSlashPlugin.ts @@ -1,12 +1,13 @@ -import { withInsertTextTriggerCombobox } from '@udecode/plate-combobox'; +import { + TriggerComboboxPlugin, + withInsertTextTriggerCombobox, +} from '@udecode/plate-combobox'; import { createPluginFactory } from '@udecode/plate-common'; -import { SlashPlugin } from './types'; - export const KEY_SLASH_COMMAND = 'slash_command'; export const ELEMENT_SLASH_INPUT = 'slash_input'; -export const createSlashPlugin = createPluginFactory({ +export const createSlashPlugin = createPluginFactory({ key: KEY_SLASH_COMMAND, withOverrides: withInsertTextTriggerCombobox, plugins: [ diff --git a/packages/slash-command/src/types.ts b/packages/slash-command/src/types.ts index 69e3819263..8af9663287 100644 --- a/packages/slash-command/src/types.ts +++ b/packages/slash-command/src/types.ts @@ -1,6 +1,3 @@ -import { TriggerComboboxPlugin } from '@udecode/plate-combobox'; import { TElement } from '@udecode/plate-common'; export interface TSlashInputElement extends TElement {} - -export interface SlashPlugin extends TriggerComboboxPlugin {} From 42f2421e88ece379fc1f9f39a4abeed26431b664 Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Thu, 2 May 2024 08:40:14 +0100 Subject: [PATCH 16/63] Refactor query -> search --- apps/www/src/registry/default/plate-ui/inline-combobox.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/www/src/registry/default/plate-ui/inline-combobox.tsx b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx index 3b25e48562..59f13c4c45 100644 --- a/apps/www/src/registry/default/plate-ui/inline-combobox.tsx +++ b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx @@ -32,13 +32,13 @@ const comboboxItemVariants = cva( const defaultMatchItem = ( { label, keywords = [] }: BaseComboboxItemWithEditor, - query: string -) => [label, ...keywords].some((keyword) => matchWords(keyword, query)); + search: string +) => [label, ...keywords].some((keyword) => matchWords(keyword, search)); interface InlineComboboxProps { trigger: string; items: TItem[]; - matchItem?: (item: TItem, query: string) => boolean; + matchItem?: (item: TItem, search: string) => boolean; renderItem?: (item: TItem) => ReactNode; renderEmpty?: ReactNode; onSelectItem?: (item: TItem) => void; From 766ab375a882af4b88c6966a86d8719bab2cb425 Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Thu, 2 May 2024 08:45:18 +0100 Subject: [PATCH 17/63] Drop label and make value user-facing --- .../default/plate-ui/inline-combobox.tsx | 6 +++--- .../default/plate-ui/slash-input-element.tsx | 19 +++++++------------ packages/combobox/src/types.ts | 1 - 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/apps/www/src/registry/default/plate-ui/inline-combobox.tsx b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx index 59f13c4c45..80974e5021 100644 --- a/apps/www/src/registry/default/plate-ui/inline-combobox.tsx +++ b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx @@ -31,9 +31,9 @@ const comboboxItemVariants = cva( ); const defaultMatchItem = ( - { label, keywords = [] }: BaseComboboxItemWithEditor, + { value, keywords = [] }: BaseComboboxItemWithEditor, search: string -) => [label, ...keywords].some((keyword) => matchWords(keyword, search)); +) => [value, ...keywords].some((keyword) => matchWords(keyword, search)); interface InlineComboboxProps { trigger: string; @@ -48,7 +48,7 @@ export const InlineCombobox = ({ trigger, items, matchItem = defaultMatchItem, - renderItem = ({ label }) => label, + renderItem = ({ value }) => value, renderEmpty, onSelectItem, }: InlineComboboxProps) => { diff --git a/apps/www/src/registry/default/plate-ui/slash-input-element.tsx b/apps/www/src/registry/default/plate-ui/slash-input-element.tsx index 1db3d44a95..f86726ce67 100644 --- a/apps/www/src/registry/default/plate-ui/slash-input-element.tsx +++ b/apps/www/src/registry/default/plate-ui/slash-input-element.tsx @@ -15,32 +15,28 @@ export type SlashCommandRule = BaseComboboxItemWithEditor & { const rules: SlashCommandRule[] = [ { - value: ELEMENT_H1, - label: 'Heading 1', + value: 'Heading 1', icon: Icons.h1, onSelect: (editor) => { toggleNodeType(editor, { activeType: ELEMENT_H1 }); }, }, { - value: ELEMENT_H2, - label: 'Heading 2', + value: 'Heading 2', icon: Icons.h2, onSelect: (editor) => { toggleNodeType(editor, { activeType: ELEMENT_H2 }); }, }, { - value: ELEMENT_H3, - label: 'Heading 3', + value: 'Heading 3', icon: Icons.h3, onSelect: (editor) => { toggleNodeType(editor, { activeType: ELEMENT_H3 }); }, }, { - value: ListStyleType.Disc, - label: 'Bulleted list', + value: 'Bulleted list', icon: Icons.ul, keywords: ['ul', 'unordered list'], onSelect: (editor) => { @@ -50,8 +46,7 @@ const rules: SlashCommandRule[] = [ }, }, { - value: ListStyleType.Decimal, - label: 'Numbered list', + value: 'Numbered list', icon: Icons.ol, keywords: ['ol', 'ordered list'], onSelect: (editor) => { @@ -76,10 +71,10 @@ export const SlashInputElement = withRef( ( + renderItem={({ icon: Icon, value }) => ( <> - {label} + {value} )} renderEmpty="No matching commands found" diff --git a/packages/combobox/src/types.ts b/packages/combobox/src/types.ts index 90e085ec22..56331afe9b 100644 --- a/packages/combobox/src/types.ts +++ b/packages/combobox/src/types.ts @@ -25,7 +25,6 @@ export type CancelComboboxInputCause = export type BaseComboboxItem = { value: string; - label: string; keywords?: string[]; onSelect?: (...args: OnSelectArgs) => void; }; From e5034c8e645c5ae028a4170db19ca8172bb2d865 Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Thu, 2 May 2024 08:50:04 +0100 Subject: [PATCH 18/63] Fix: SlashCommandRule doesn't need exporting --- apps/www/src/registry/default/plate-ui/slash-input-element.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/www/src/registry/default/plate-ui/slash-input-element.tsx b/apps/www/src/registry/default/plate-ui/slash-input-element.tsx index f86726ce67..9a28476e20 100644 --- a/apps/www/src/registry/default/plate-ui/slash-input-element.tsx +++ b/apps/www/src/registry/default/plate-ui/slash-input-element.tsx @@ -9,7 +9,7 @@ import { Icons } from '@/components/icons'; import { InlineCombobox } from './inline-combobox'; -export type SlashCommandRule = BaseComboboxItemWithEditor & { +type SlashCommandRule = BaseComboboxItemWithEditor & { icon: ComponentType>; }; From a20be8989146118c8d22f391faf436081a0b742f Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Thu, 2 May 2024 11:41:21 +0100 Subject: [PATCH 19/63] Refactor withInsertTextTriggerCombobox -> withTriggerCombobox --- packages/combobox/src/index.ts | 2 +- ...ithInsertTextTriggerCombobox.ts => withTriggerCombobox.ts} | 2 +- packages/slash-command/src/createSlashPlugin.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename packages/combobox/src/{withInsertTextTriggerCombobox.ts => withTriggerCombobox.ts} (96%) diff --git a/packages/combobox/src/index.ts b/packages/combobox/src/index.ts index 479d58af77..8a5c5ba16e 100644 --- a/packages/combobox/src/index.ts +++ b/packages/combobox/src/index.ts @@ -3,7 +3,7 @@ */ export * from './types'; -export * from './withInsertTextTriggerCombobox'; +export * from './withTriggerCombobox'; export * from './hooks/index'; export * from './legacy-combobox-delete-me/index'; export * from './utils/index'; diff --git a/packages/combobox/src/withInsertTextTriggerCombobox.ts b/packages/combobox/src/withTriggerCombobox.ts similarity index 96% rename from packages/combobox/src/withInsertTextTriggerCombobox.ts rename to packages/combobox/src/withTriggerCombobox.ts index 736cd21b30..81591180ac 100644 --- a/packages/combobox/src/withInsertTextTriggerCombobox.ts +++ b/packages/combobox/src/withTriggerCombobox.ts @@ -10,7 +10,7 @@ import { import { TriggerComboboxPlugin } from './types'; -export const withInsertTextTriggerCombobox = < +export const withTriggerCombobox = < V extends Value = Value, E extends PlateEditor = PlateEditor, >( diff --git a/packages/slash-command/src/createSlashPlugin.ts b/packages/slash-command/src/createSlashPlugin.ts index c0ba40478d..06b1055d33 100644 --- a/packages/slash-command/src/createSlashPlugin.ts +++ b/packages/slash-command/src/createSlashPlugin.ts @@ -1,6 +1,6 @@ import { TriggerComboboxPlugin, - withInsertTextTriggerCombobox, + withTriggerCombobox, } from '@udecode/plate-combobox'; import { createPluginFactory } from '@udecode/plate-common'; @@ -9,7 +9,7 @@ export const ELEMENT_SLASH_INPUT = 'slash_input'; export const createSlashPlugin = createPluginFactory({ key: KEY_SLASH_COMMAND, - withOverrides: withInsertTextTriggerCombobox, + withOverrides: withTriggerCombobox, plugins: [ { key: ELEMENT_SLASH_INPUT, From f5e055334f3df1e9e887dbedb4e0183cd40fa9f9 Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Thu, 2 May 2024 11:44:56 +0100 Subject: [PATCH 20/63] Refactor TriggerComboboxPlugin options --- packages/combobox/src/types.ts | 10 ++++------ packages/combobox/src/withTriggerCombobox.ts | 16 +++++++--------- packages/slash-command/src/createSlashPlugin.ts | 14 ++++++-------- 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/packages/combobox/src/types.ts b/packages/combobox/src/types.ts index 56331afe9b..1a9d3132aa 100644 --- a/packages/combobox/src/types.ts +++ b/packages/combobox/src/types.ts @@ -1,12 +1,10 @@ import { PlateEditor, TElement } from '@udecode/plate-common'; export interface TriggerComboboxPlugin { - combobox?: { - trigger?: string; - triggerPreviousCharPattern?: RegExp; - query?: (editor: PlateEditor) => boolean; - createInputNode?: () => TElement; - }; + trigger?: string; + triggerPreviousCharPattern?: RegExp; + triggerQuery?: (editor: PlateEditor) => boolean; + createComboboxInput?: () => TElement; } export type ComboboxInputCursorState = { diff --git a/packages/combobox/src/withTriggerCombobox.ts b/packages/combobox/src/withTriggerCombobox.ts index 81591180ac..ef7ee5c36d 100644 --- a/packages/combobox/src/withTriggerCombobox.ts +++ b/packages/combobox/src/withTriggerCombobox.ts @@ -18,12 +18,10 @@ export const withTriggerCombobox = < { type, options: { - combobox: { - trigger, - triggerPreviousCharPattern, - query, - createInputNode, - } = {}, + trigger, + triggerPreviousCharPattern, + triggerQuery, + createComboboxInput, }, }: WithPlatePlugin ) => { @@ -33,7 +31,7 @@ export const withTriggerCombobox = < if ( !editor.selection || text !== trigger || - (query && !query(editor as PlateEditor)) + (triggerQuery && !triggerQuery(editor as PlateEditor)) ) { return insertText(text); } @@ -52,8 +50,8 @@ export const withTriggerCombobox = < triggerPreviousCharPattern?.test(previousChar); if (matchesPreviousCharPattern && text === trigger) { - const inputNode: TElement = createInputNode - ? createInputNode() + const inputNode: TElement = createComboboxInput + ? createComboboxInput() : { type, children: [{ text: '' }] }; return editor.insertNode(inputNode); diff --git a/packages/slash-command/src/createSlashPlugin.ts b/packages/slash-command/src/createSlashPlugin.ts index 06b1055d33..2864c4da4a 100644 --- a/packages/slash-command/src/createSlashPlugin.ts +++ b/packages/slash-command/src/createSlashPlugin.ts @@ -19,13 +19,11 @@ export const createSlashPlugin = createPluginFactory({ }, ], options: { - combobox: { - trigger: '/', - triggerPreviousCharPattern: /^\s?$/, - createInputNode: () => ({ - type: ELEMENT_SLASH_INPUT, - children: [{ text: '' }], - }), - }, + trigger: '/', + triggerPreviousCharPattern: /^\s?$/, + createComboboxInput: () => ({ + type: ELEMENT_SLASH_INPUT, + children: [{ text: '' }], + }), }, }); From 26e92a7956dfb02c217d564717628c4a2386fdc9 Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Thu, 2 May 2024 20:01:28 +0100 Subject: [PATCH 21/63] Revert "Fix: www app uses separate slate-react to packages, breaking useSelected" This reverts commit 869f8689f5d913946854fa26c6790857fdc14d16. --- apps/www/package.json | 1 + yarn.lock | 1 + 2 files changed, 2 insertions(+) diff --git a/apps/www/package.json b/apps/www/package.json index e5fb76276c..1a77218c7d 100644 --- a/apps/www/package.json +++ b/apps/www/package.json @@ -136,6 +136,7 @@ "slate": "0.102.0", "slate-history": "0.100.0", "slate-hyperscript": "0.100.0", + "slate-react": "0.102.0", "slate-test-utils": "1.3.2", "sonner": "^1.4.32", "tailwind-merge": "^2.2.2", diff --git a/yarn.lock b/yarn.lock index 572704dd90..9dc570fd22 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21478,6 +21478,7 @@ __metadata: slate: "npm:0.102.0" slate-history: "npm:0.100.0" slate-hyperscript: "npm:0.100.0" + slate-react: "npm:0.102.0" slate-test-utils: "npm:1.3.2" sonner: "npm:^1.4.32" tailwind-merge: "npm:^2.2.2" From 7cca80c83121208d5af7138012b22c77dff8b4a6 Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Thu, 2 May 2024 20:06:10 +0100 Subject: [PATCH 22/63] Rename match -> filter --- .../default/plate-ui/inline-combobox.tsx | 14 ++-- .../combobox/src/utils/filterWords.spec.ts | 71 +++++++++++++++++++ .../utils/{matchWords.ts => filterWords.ts} | 6 +- packages/combobox/src/utils/index.ts | 2 +- .../combobox/src/utils/matchWords.spec.ts | 71 ------------------- 5 files changed, 82 insertions(+), 82 deletions(-) create mode 100644 packages/combobox/src/utils/filterWords.spec.ts rename packages/combobox/src/utils/{matchWords.ts => filterWords.ts} (91%) delete mode 100644 packages/combobox/src/utils/matchWords.spec.ts diff --git a/apps/www/src/registry/default/plate-ui/inline-combobox.tsx b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx index 80974e5021..6a674b436a 100644 --- a/apps/www/src/registry/default/plate-ui/inline-combobox.tsx +++ b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx @@ -8,7 +8,7 @@ import { } from '@ariakit/react'; import { BaseComboboxItemWithEditor, - matchWords, + filterWords, useComboboxInput, useHTMLInputCursorState, } from '@udecode/plate-combobox'; @@ -30,15 +30,15 @@ const comboboxItemVariants = cva( } ); -const defaultMatchItem = ( +const defaultFilter = ( { value, keywords = [] }: BaseComboboxItemWithEditor, search: string -) => [value, ...keywords].some((keyword) => matchWords(keyword, search)); +) => [value, ...keywords].some((keyword) => filterWords(keyword, search)); interface InlineComboboxProps { trigger: string; items: TItem[]; - matchItem?: (item: TItem, search: string) => boolean; + filter?: (item: TItem, search: string) => boolean; renderItem?: (item: TItem) => ReactNode; renderEmpty?: ReactNode; onSelectItem?: (item: TItem) => void; @@ -47,7 +47,7 @@ interface InlineComboboxProps { export const InlineCombobox = ({ trigger, items, - matchItem = defaultMatchItem, + filter = defaultFilter, renderItem = ({ value }) => value, renderEmpty, onSelectItem, @@ -58,8 +58,8 @@ export const InlineCombobox = ({ const cursorState = useHTMLInputCursorState(inputRef); const filteredItems = useMemo( - () => items.filter((item) => matchItem(item, value)), - [items, matchItem, value] + () => items.filter((item) => filter(item, value)), + [items, filter, value] ); const { removeInput, props: inputProps } = useComboboxInput({ diff --git a/packages/combobox/src/utils/filterWords.spec.ts b/packages/combobox/src/utils/filterWords.spec.ts new file mode 100644 index 0000000000..2c939fa40d --- /dev/null +++ b/packages/combobox/src/utils/filterWords.spec.ts @@ -0,0 +1,71 @@ +import { filterWords, FilterWordsOptions } from './filterWords'; + +describe('filterWords', () => { + describe('with default options', () => { + describe('single word', () => { + it('matches simple prefix', () => { + expect(filterWords('hello', 'he')).toBe(true); + }); + + it('does not match non-prefix', () => { + expect(filterWords('hello', 'lo')).toBe(false); + }); + + it('is case-insensitive', () => { + expect(filterWords('hello', 'HE')).toBe(true); + }); + + it('is diacritic-insensitive', () => { + expect(filterWords('hello', 'hé')).toBe(true); + }); + }); + + describe('multiple words', () => { + it('matches when all words in query match', () => { + expect(filterWords('hello world', 'world hello')).toBe(true); + expect(filterWords('hello world', 'world')).toBe(true); + }); + + it('does not match when not all words in query match', () => { + expect(filterWords('hello world', 'hello other')).toBe(false); + }); + + it('allows prefix for last word', () => { + expect(filterWords('hello world', 'world he')).toBe(true); + }); + + it('does not allow prefix for non-last word', () => { + expect(filterWords('hello world', 'wor hello')).toBe(false); + }); + }); + }); + + describe('with prefix mode disabled', () => { + const options: FilterWordsOptions = { prefixMode: 'none' }; + + it('only matches whole words', () => { + expect(filterWords('hello world', 'wor', options)).toBe(false); + expect(filterWords('hello world', 'world', options)).toBe(true); + }); + }); + + describe('with prefix mode set to all words', () => { + const options: FilterWordsOptions = { prefixMode: 'all-words' }; + + it('allows prefix for all words', () => { + expect(filterWords('hello world', 'wor hel', options)).toBe(true); + }); + }); + + describe('with word quantifier set to match any', () => { + const options: FilterWordsOptions = { wordQuantifier: 'match-any' }; + + it('matches when any word in query matches', () => { + expect(filterWords('hello world', 'other hello', options)).toBe(true); + }); + + it('does not match when no word in query matches', () => { + expect(filterWords('hello world', 'other other', options)).toBe(false); + }); + }); +}); diff --git a/packages/combobox/src/utils/matchWords.ts b/packages/combobox/src/utils/filterWords.ts similarity index 91% rename from packages/combobox/src/utils/matchWords.ts rename to packages/combobox/src/utils/filterWords.ts index e9be6c9c96..4816179f92 100644 --- a/packages/combobox/src/utils/matchWords.ts +++ b/packages/combobox/src/utils/filterWords.ts @@ -1,15 +1,15 @@ -export interface MatchWordsOptions { +export interface FilterWordsOptions { prefixMode?: 'none' | 'all-words' | 'last-word'; wordQuantifier?: 'match-all' | 'match-any'; } -export const matchWords = ( +export const filterWords = ( haystack: string, needle: string, { prefixMode = 'last-word', wordQuantifier = 'match-all', - }: MatchWordsOptions = {} + }: FilterWordsOptions = {} ): boolean => { const haystackWords = haystack.trim().split(/\s+/); const needleWords = needle.trim().split(/\s+/); diff --git a/packages/combobox/src/utils/index.ts b/packages/combobox/src/utils/index.ts index 09edaea9dc..7afc6fa598 100644 --- a/packages/combobox/src/utils/index.ts +++ b/packages/combobox/src/utils/index.ts @@ -2,4 +2,4 @@ * @file Automatically generated by barrelsby. */ -export * from './matchWords'; +export * from './filterWords'; diff --git a/packages/combobox/src/utils/matchWords.spec.ts b/packages/combobox/src/utils/matchWords.spec.ts deleted file mode 100644 index 8376a0cf56..0000000000 --- a/packages/combobox/src/utils/matchWords.spec.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { matchWords, MatchWordsOptions } from './matchWords'; - -describe('matchWords', () => { - describe('with default options', () => { - describe('single word', () => { - it('matches simple prefix', () => { - expect(matchWords('hello', 'he')).toBe(true); - }); - - it('does not match non-prefix', () => { - expect(matchWords('hello', 'lo')).toBe(false); - }); - - it('is case-insensitive', () => { - expect(matchWords('hello', 'HE')).toBe(true); - }); - - it('is diacritic-insensitive', () => { - expect(matchWords('hello', 'hé')).toBe(true); - }); - }); - - describe('multiple words', () => { - it('matches when all words in query match', () => { - expect(matchWords('hello world', 'world hello')).toBe(true); - expect(matchWords('hello world', 'world')).toBe(true); - }); - - it('does not match when not all words in query match', () => { - expect(matchWords('hello world', 'hello other')).toBe(false); - }); - - it('allows prefix for last word', () => { - expect(matchWords('hello world', 'world he')).toBe(true); - }); - - it('does not allow prefix for non-last word', () => { - expect(matchWords('hello world', 'wor hello')).toBe(false); - }); - }); - }); - - describe('with prefix mode disabled', () => { - const options: MatchWordsOptions = { prefixMode: 'none' }; - - it('only matches whole words', () => { - expect(matchWords('hello world', 'wor', options)).toBe(false); - expect(matchWords('hello world', 'world', options)).toBe(true); - }); - }); - - describe('with prefix mode set to all words', () => { - const options: MatchWordsOptions = { prefixMode: 'all-words' }; - - it('allows prefix for all words', () => { - expect(matchWords('hello world', 'wor hel', options)).toBe(true); - }); - }); - - describe('with word quantifier set to match any', () => { - const options: MatchWordsOptions = { wordQuantifier: 'match-any' }; - - it('matches when any word in query matches', () => { - expect(matchWords('hello world', 'other hello', options)).toBe(true); - }); - - it('does not match when no word in query matches', () => { - expect(matchWords('hello world', 'other other', options)).toBe(false); - }); - }); -}); From 13f4fa945bdf0373a7fee3ce18a21c25c0ee408f Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Thu, 2 May 2024 20:58:41 +0100 Subject: [PATCH 23/63] Refactor InlineCombobox into multiple components --- .../default/plate-ui/inline-combobox.tsx | 326 +++++++++++++----- .../default/plate-ui/slash-input-element.tsx | 56 ++- packages/combobox/src/types.ts | 8 - 3 files changed, 282 insertions(+), 108 deletions(-) diff --git a/apps/www/src/registry/default/plate-ui/inline-combobox.tsx b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx index 6a674b436a..4255956441 100644 --- a/apps/www/src/registry/default/plate-ui/inline-combobox.tsx +++ b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx @@ -1,67 +1,80 @@ -import React, { ReactNode, startTransition, useMemo, useState } from 'react'; +import React, { + createContext, + forwardRef, + HTMLAttributes, + ReactNode, + RefObject, + startTransition, + useContext, + useEffect, + useMemo, + useReducer, + useRef, + useState, +} from 'react'; import { Combobox, ComboboxItem, + ComboboxItemProps, ComboboxPopover, ComboboxProvider, Portal, } from '@ariakit/react'; +import { cn } from '@udecode/cn'; import { - BaseComboboxItemWithEditor, filterWords, useComboboxInput, + UseComboboxInputResult, useHTMLInputCursorState, } from '@udecode/plate-combobox'; -import { insertText, moveSelection, useEditorRef } from '@udecode/plate-common'; +import { + insertText, + moveSelection, + useComposedRef, + useEditorRef, +} from '@udecode/plate-common'; import { cva } from 'class-variance-authority'; -const comboboxItemVariants = cva( - 'relative flex h-9 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none', - { - variants: { - interactive: { - true: 'cursor-pointer transition-colors hover:bg-accent hover:text-accent-foreground data-[active-item=true]:bg-accent data-[active-item=true]:text-accent-foreground', - false: '', - }, - }, - defaultVariants: { - interactive: true, - }, - } +type FilterFn = ( + item: { value: string; keywords?: string[] }, + search: string +) => boolean; + +interface InlineComboboxContextValue { + trigger: string; + filter: FilterFn | false; + value: string; + inputRef: RefObject; + inputProps: UseComboboxInputResult['props']; + removeInput: UseComboboxInputResult['removeInput']; + visibleCount: number; + dispatchVisible: (action: 'increment' | 'decrement') => void; + setHasEmpty: (hasEmpty: boolean) => void; +} + +const InlineComboboxContext = createContext( + null as any ); -const defaultFilter = ( - { value, keywords = [] }: BaseComboboxItemWithEditor, - search: string -) => [value, ...keywords].some((keyword) => filterWords(keyword, search)); +const defaultFilter: FilterFn = ({ value, keywords = [] }, search) => + [value, ...keywords].some((keyword) => filterWords(keyword, search)); -interface InlineComboboxProps { +interface InlineComboboxProps { trigger: string; - items: TItem[]; - filter?: (item: TItem, search: string) => boolean; - renderItem?: (item: TItem) => ReactNode; - renderEmpty?: ReactNode; - onSelectItem?: (item: TItem) => void; + filter?: FilterFn | false; + children: ReactNode; } -export const InlineCombobox = ({ +const InlineCombobox = ({ trigger, - items, filter = defaultFilter, - renderItem = ({ value }) => value, - renderEmpty, - onSelectItem, -}: InlineComboboxProps) => { + children, +}: InlineComboboxProps) => { const editor = useEditorRef(); const [value, setValue] = useState(''); const inputRef = React.useRef(null); const cursorState = useHTMLInputCursorState(inputRef); - const filteredItems = useMemo( - () => items.filter((item) => filter(item, value)), - [items, filter, value] - ); - const { removeInput, props: inputProps } = useComboboxInput({ ref: inputRef, cursorState, @@ -79,6 +92,65 @@ export const InlineCombobox = ({ }, }); + const [visibleCount, dispatchVisible] = useReducer( + (state: number, action: 'increment' | 'decrement') => + state + (action === 'increment' ? 1 : -1), + 0 + ); + + const [hasEmpty, setHasEmpty] = useState(false); + + const contextValue: InlineComboboxContextValue = useMemo( + () => ({ + trigger, + filter, + value, + inputRef, + inputProps, + removeInput, + visibleCount, + dispatchVisible, + setHasEmpty, + }), + [ + trigger, + filter, + value, + inputRef, + inputProps, + removeInput, + visibleCount, + setHasEmpty, + ] + ); + + return ( + + 0 || hasEmpty} + setValue={(newValue) => startTransition(() => setValue(newValue))} + > + + {children} + + + + ); +}; + +const InlineComboboxInput = forwardRef< + HTMLInputElement, + HTMLAttributes +>((props, propRef) => { + const { + inputRef: contextRef, + inputProps, + value, + trigger, + } = useContext(InlineComboboxContext); + + const ref = useComposedRef(propRef, contextRef); + /** * To create an auto-resizing input, we render a visually hidden span * containing the input value and position the input element on top of it. @@ -87,55 +159,143 @@ export const InlineCombobox = ({ */ return ( - + <> {trigger} - 0 || renderEmpty !== undefined} - setValue={(newValue) => startTransition(() => setValue(newValue))} - > - - - - + + - {/* Portal prevents CSS from leaking into popover */} - - - {filteredItems.map((item) => ( - { - removeInput(true); - item.onSelect?.(editor); - onSelectItem?.(item); - }} - > - {renderItem(item)} - - ))} - - {filteredItems.length === 0 && renderEmpty && ( -
- {renderEmpty} -
- )} -
-
-
-
+ + + + ); +}); + +InlineComboboxInput.displayName = 'InlineComboboxInput'; + +const InlineComboboxContent: typeof ComboboxPopover = ({ + className, + ...props +}) => { + // Portal prevents CSS from leaking into popover + return ( + + + ); }; + +const comboboxItemVariants = cva( + 'relative flex h-9 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none', + { + variants: { + interactive: { + true: 'cursor-pointer transition-colors hover:bg-accent hover:text-accent-foreground data-[active-item=true]:bg-accent data-[active-item=true]:text-accent-foreground', + false: '', + }, + }, + defaultVariants: { + interactive: true, + }, + } +); + +export type InlineComboboxItemProps = ComboboxItemProps & + Required> & { + keywords?: string[]; + }; + +const InlineComboboxItem = ({ + className, + onClick, + keywords, + ...props +}: InlineComboboxItemProps) => { + const { value } = props; + + const { + value: search, + filter, + dispatchVisible, + removeInput, + } = useContext(InlineComboboxContext); + + const visible = useMemo( + () => !filter || filter({ value, keywords }, search), + [filter, value, keywords, search] + ); + + const previousVisibleRef = useRef(false); + + useEffect(() => { + if (visible !== previousVisibleRef.current) { + dispatchVisible(visible ? 'increment' : 'decrement'); + } + + previousVisibleRef.current = visible; + }, [dispatchVisible, visible]); + + if (!visible) return null; + + return ( + { + removeInput(true); + onClick?.(event); + }} + {...props} + /> + ); +}; + +const InlineComboboxEmpty = ({ + className, + children, +}: HTMLAttributes) => { + const { visibleCount, setHasEmpty } = useContext(InlineComboboxContext); + + useEffect(() => { + setHasEmpty(true); + + return () => { + setHasEmpty(false); + }; + }, [setHasEmpty]); + + if (visibleCount > 0) return null; + + return ( +
+ {children} +
+ ); +}; + +export { + InlineCombobox, + InlineComboboxInput, + InlineComboboxContent, + InlineComboboxItem, + InlineComboboxEmpty, +}; diff --git a/apps/www/src/registry/default/plate-ui/slash-input-element.tsx b/apps/www/src/registry/default/plate-ui/slash-input-element.tsx index 9a28476e20..dd2107b8e7 100644 --- a/apps/www/src/registry/default/plate-ui/slash-input-element.tsx +++ b/apps/www/src/registry/default/plate-ui/slash-input-element.tsx @@ -1,17 +1,29 @@ import React, { ComponentType, SVGProps } from 'react'; import { withRef } from '@udecode/cn'; -import { BaseComboboxItemWithEditor } from '@udecode/plate-combobox'; -import { PlateElement, toggleNodeType } from '@udecode/plate-common'; +import { + PlateEditor, + PlateElement, + toggleNodeType, +} from '@udecode/plate-common'; import { ELEMENT_H1, ELEMENT_H2, ELEMENT_H3 } from '@udecode/plate-heading'; import { ListStyleType, toggleIndentList } from '@udecode/plate-indent-list'; import { Icons } from '@/components/icons'; -import { InlineCombobox } from './inline-combobox'; +import { + InlineCombobox, + InlineComboboxContent, + InlineComboboxEmpty, + InlineComboboxInput, + InlineComboboxItem, +} from './inline-combobox'; -type SlashCommandRule = BaseComboboxItemWithEditor & { +interface SlashCommandRule { + value: string; + keywords?: string[]; icon: ComponentType>; -}; + onSelect: (editor: PlateEditor) => void; +} const rules: SlashCommandRule[] = [ { @@ -59,7 +71,7 @@ const rules: SlashCommandRule[] = [ export const SlashInputElement = withRef( ({ className, ...props }, ref) => { - const { children, element } = props; + const { children, element, editor } = props; return ( ( data-slate-value={element.value} {...props} > - ( - <> - - {value} - - )} - renderEmpty="No matching commands found" - /> + + + + + + No matching commands found + + + {rules.map(({ value, keywords, icon: Icon, onSelect }) => ( + onSelect?.(editor)} + > + + {value} + + ))} + + {children} diff --git a/packages/combobox/src/types.ts b/packages/combobox/src/types.ts index 1a9d3132aa..9d1b13f764 100644 --- a/packages/combobox/src/types.ts +++ b/packages/combobox/src/types.ts @@ -20,11 +20,3 @@ export type CancelComboboxInputCause = | 'arrowRight' | 'deselect' | 'blur'; - -export type BaseComboboxItem = { - value: string; - keywords?: string[]; - onSelect?: (...args: OnSelectArgs) => void; -}; - -export type BaseComboboxItemWithEditor = BaseComboboxItem<[PlateEditor]>; From 2c86fbf7f13d7824b9db52e2106ad15150419718 Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Thu, 2 May 2024 21:08:57 +0100 Subject: [PATCH 24/63] Fix: InlineComboboxItem className should be merged --- apps/www/src/registry/default/plate-ui/inline-combobox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/www/src/registry/default/plate-ui/inline-combobox.tsx b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx index 4255956441..464a450f6e 100644 --- a/apps/www/src/registry/default/plate-ui/inline-combobox.tsx +++ b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx @@ -257,7 +257,7 @@ const InlineComboboxItem = ({ return ( { removeInput(true); onClick?.(event); From 538cc020b68b5d81d0576584619b7e396b8c13ef Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Thu, 2 May 2024 21:09:23 +0100 Subject: [PATCH 25/63] Remove unnecessary `?.` --- apps/www/src/registry/default/plate-ui/slash-input-element.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/www/src/registry/default/plate-ui/slash-input-element.tsx b/apps/www/src/registry/default/plate-ui/slash-input-element.tsx index dd2107b8e7..3bbb867230 100644 --- a/apps/www/src/registry/default/plate-ui/slash-input-element.tsx +++ b/apps/www/src/registry/default/plate-ui/slash-input-element.tsx @@ -93,7 +93,7 @@ export const SlashInputElement = withRef( key={value} value={value} keywords={keywords} - onClick={() => onSelect?.(editor)} + onClick={() => onSelect(editor)} > {value} From 4a5c168e96c56dacc0b823c008f07b6761b337e6 Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Fri, 3 May 2024 13:37:22 +0100 Subject: [PATCH 26/63] Use new combobox API for mentions --- .../docs/components/mention-combobox.mdx | 65 -- .../src/lib/plate/demo/values/mentionables.ts | 8 +- .../default/example/playground-demo.tsx | 6 - .../default/plate-ui/inline-combobox.tsx | 30 +- .../default/plate-ui/mention-combobox.tsx | 36 - .../plate-ui/mention-input-element.tsx | 91 ++- .../combobox/src/hooks/useComboboxInput.ts | 32 +- packages/combobox/src/types.ts | 4 +- .../combobox/src/withTriggerCombobox.spec.tsx | 222 ++++++ packages/combobox/src/withTriggerCombobox.ts | 18 +- .../__tests__/createEditorWithMentions.tsx | 44 -- packages/mention/src/createMentionPlugin.ts | 29 +- .../mention/src/getMentionOnSelectItem.ts | 92 +-- packages/mention/src/handlers/index.ts | 5 - .../handlers/mentionOnKeyDownHandler.spec.tsx | 34 - .../src/handlers/mentionOnKeyDownHandler.ts | 30 - packages/mention/src/index.ts | 4 - .../mention/src/queries/findMentionInput.ts | 19 - packages/mention/src/queries/index.ts | 7 - .../mention/src/queries/isNodeMentionInput.ts | 16 - .../src/queries/isSelectionInMentionInput.ts | 7 - packages/mention/src/transforms/index.ts | 5 - .../src/transforms/removeMentionInput.ts | 30 - packages/mention/src/types.ts | 28 +- packages/mention/src/withMention.spec.tsx | 636 ------------------ packages/mention/src/withMention.ts | 205 ------ .../slate/src/interfaces/element/TElement.ts | 20 +- .../slate/src/interfaces/node/TAncestor.ts | 12 +- .../slate/src/interfaces/node/TDescendant.ts | 8 +- packages/slate/src/interfaces/node/TNode.ts | 4 +- packages/slate/src/interfaces/text/TText.ts | 21 +- 31 files changed, 446 insertions(+), 1322 deletions(-) delete mode 100644 apps/www/content/docs/components/mention-combobox.mdx delete mode 100644 apps/www/src/registry/default/plate-ui/mention-combobox.tsx create mode 100644 packages/combobox/src/withTriggerCombobox.spec.tsx delete mode 100644 packages/mention/src/__tests__/createEditorWithMentions.tsx delete mode 100644 packages/mention/src/handlers/index.ts delete mode 100644 packages/mention/src/handlers/mentionOnKeyDownHandler.spec.tsx delete mode 100644 packages/mention/src/handlers/mentionOnKeyDownHandler.ts delete mode 100644 packages/mention/src/queries/findMentionInput.ts delete mode 100644 packages/mention/src/queries/index.ts delete mode 100644 packages/mention/src/queries/isNodeMentionInput.ts delete mode 100644 packages/mention/src/queries/isSelectionInMentionInput.ts delete mode 100644 packages/mention/src/transforms/index.ts delete mode 100644 packages/mention/src/transforms/removeMentionInput.ts delete mode 100644 packages/mention/src/withMention.spec.tsx delete mode 100644 packages/mention/src/withMention.ts diff --git a/apps/www/content/docs/components/mention-combobox.mdx b/apps/www/content/docs/components/mention-combobox.mdx deleted file mode 100644 index 1687d201d4..0000000000 --- a/apps/www/content/docs/components/mention-combobox.mdx +++ /dev/null @@ -1,65 +0,0 @@ ---- -title: Mention Combobox -description: Enter and select mentions or references using a combination of text input and a dropdown menu. -component: true -docs: - - route: /docs/combobox - title: Combobox - - route: /docs/mention - title: Mention ---- - -## Installation - - - - -CLI -Manual - - - -```bash -npx @udecode/plate-ui@latest add mention-combobox -``` - - - - - - - - - -Install the following dependencies: - -- [Combobox](/docs/combobox) -- [Mention](/docs/mention) - - - - - -Copy and paste the following code into your project. - - - - - - - -Update the import paths to match your project setup. - - - - - - - - - -## Examples - - - - diff --git a/apps/www/src/lib/plate/demo/values/mentionables.ts b/apps/www/src/lib/plate/demo/values/mentionables.ts index f99868188e..ecc5517637 100644 --- a/apps/www/src/lib/plate/demo/values/mentionables.ts +++ b/apps/www/src/lib/plate/demo/values/mentionables.ts @@ -1,6 +1,10 @@ -import { TComboboxItem } from '@udecode/plate-combobox'; +import { TMentionItemBase } from '@udecode/plate-mention'; -export const MENTIONABLES: TComboboxItem[] = [ +export interface MyMentionItem extends TMentionItemBase { + key: string; +} + +export const MENTIONABLES: MyMentionItem[] = [ { key: '0', text: 'Aayla Secura' }, { key: '1', text: 'Adi Gallia' }, { diff --git a/apps/www/src/registry/default/example/playground-demo.tsx b/apps/www/src/registry/default/example/playground-demo.tsx index b7f2e83cb5..009d9a6f5d 100644 --- a/apps/www/src/registry/default/example/playground-demo.tsx +++ b/apps/www/src/registry/default/example/playground-demo.tsx @@ -20,7 +20,6 @@ import { selectOnBackspacePlugin } from '@/plate/demo/plugins/selectOnBackspaceP import { softBreakPlugin } from '@/plate/demo/plugins/softBreakPlugin'; import { tabbablePlugin } from '@/plate/demo/plugins/tabbablePlugin'; import { trailingBlockPlugin } from '@/plate/demo/plugins/trailingBlockPlugin'; -import { MENTIONABLES } from '@/plate/demo/values/mentionables'; import { usePlaygroundValue } from '@/plate/demo/values/usePlaygroundValue'; import { cn } from '@udecode/cn'; import { createAlignPlugin } from '@udecode/plate-alignment'; @@ -128,7 +127,6 @@ import { TodoLi, TodoMarker, } from '@/registry/default/plate-ui/indent-todo-marker-component'; -import { MentionCombobox } from '@/registry/default/plate-ui/mention-combobox'; export const usePlaygroundPlugins = ({ id, @@ -429,10 +427,6 @@ export default function PlaygroundDemo({ id }: { id?: ValueId }) { )} - {isEnabled('mention', id, enabled['mention-combobox']) && ( - - )} - {isEnabled('cursoroverlay', id) && ( )} diff --git a/apps/www/src/registry/default/plate-ui/inline-combobox.tsx b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx index 464a450f6e..4f3e9129e8 100644 --- a/apps/www/src/registry/default/plate-ui/inline-combobox.tsx +++ b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx @@ -42,6 +42,7 @@ type FilterFn = ( interface InlineComboboxContextValue { trigger: string; + showTrigger: boolean; filter: FilterFn | false; value: string; inputRef: RefObject; @@ -60,24 +61,35 @@ const defaultFilter: FilterFn = ({ value, keywords = [] }, search) => [value, ...keywords].some((keyword) => filterWords(keyword, search)); interface InlineComboboxProps { + value?: string; + setValue?: (value: string) => void; trigger: string; + showTrigger?: boolean; filter?: FilterFn | false; children: ReactNode; } const InlineCombobox = ({ + value: valueProp, + setValue: setValueProp, trigger, + showTrigger = true, filter = defaultFilter, children, }: InlineComboboxProps) => { const editor = useEditorRef(); - const [value, setValue] = useState(''); const inputRef = React.useRef(null); const cursorState = useHTMLInputCursorState(inputRef); + const [valueState, setValueState] = useState(''); + const hasValueProp = valueProp !== undefined; + const value = hasValueProp ? valueProp : valueState; + const setValue = hasValueProp ? setValueProp ?? (() => {}) : setValueState; + const { removeInput, props: inputProps } = useComboboxInput({ ref: inputRef, cursorState, + cancelInputOnBlur: false, onCancelInput: (cause) => { if (cause !== 'backspace') { insertText(editor, trigger + value); @@ -103,6 +115,7 @@ const InlineCombobox = ({ const contextValue: InlineComboboxContextValue = useMemo( () => ({ trigger, + showTrigger, filter, value, inputRef, @@ -114,6 +127,7 @@ const InlineCombobox = ({ }), [ trigger, + showTrigger, filter, value, inputRef, @@ -141,12 +155,13 @@ const InlineCombobox = ({ const InlineComboboxInput = forwardRef< HTMLInputElement, HTMLAttributes ->((props, propRef) => { +>(({ className, ...props }, propRef) => { const { inputRef: contextRef, inputProps, value, trigger, + showTrigger, } = useContext(InlineComboboxContext); const ref = useComposedRef(propRef, contextRef); @@ -160,21 +175,24 @@ const InlineComboboxInput = forwardRef< return ( <> - {trigger} + {showTrigger && trigger} - + diff --git a/apps/www/src/registry/default/plate-ui/mention-combobox.tsx b/apps/www/src/registry/default/plate-ui/mention-combobox.tsx deleted file mode 100644 index 255ba9c242..0000000000 --- a/apps/www/src/registry/default/plate-ui/mention-combobox.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react'; -import { ComboboxProps } from '@udecode/plate-combobox'; -import { getPluginOptions, useEditorRef } from '@udecode/plate-common'; -import { - ELEMENT_MENTION, - getMentionOnSelectItem, - MentionPlugin, -} from '@udecode/plate-mention'; - -import { Combobox } from './combobox'; - -export function MentionCombobox({ - pluginKey = ELEMENT_MENTION, - id = pluginKey, - ...props -}: Partial & { - pluginKey?: string; -}) { - const editor = useEditorRef(); - - const { trigger } = getPluginOptions(editor, pluginKey); - - return ( -
e.preventDefault()}> - -
- ); -} diff --git a/apps/www/src/registry/default/plate-ui/mention-input-element.tsx b/apps/www/src/registry/default/plate-ui/mention-input-element.tsx index c59dc27efa..bedef401f6 100644 --- a/apps/www/src/registry/default/plate-ui/mention-input-element.tsx +++ b/apps/www/src/registry/default/plate-ui/mention-input-element.tsx @@ -1,33 +1,64 @@ -import React from 'react'; +import React, { useState } from 'react'; import { cn, withRef } from '@udecode/cn'; -import { getHandler, PlateElement } from '@udecode/plate-common'; -import { useFocused, useSelected } from 'slate-react'; +import { PlateElement } from '@udecode/plate-common'; +import { getMentionOnSelectItem } from '@udecode/plate-mention'; -export const MentionInputElement = withRef< - typeof PlateElement, - { - onClick?: (mentionNode: any) => void; +import { MENTIONABLES } from '@/lib/plate/demo/values/mentionables'; + +import { + InlineCombobox, + InlineComboboxContent, + InlineComboboxEmpty, + InlineComboboxInput, + InlineComboboxItem, +} from './inline-combobox'; + +const onSelectItem = getMentionOnSelectItem(); + +export const MentionInputElement = withRef( + ({ className, ...props }, ref) => { + const { children, element, editor } = props; + const [search, setSearch] = useState(''); + + return ( + + + + + + + + No results found + + {MENTIONABLES.map((item) => ( + onSelectItem(editor, item, search)} + > + {item.text} + + ))} + + + + {children} + + ); } ->(({ className, onClick, ...props }, ref) => { - const { children, element } = props; - - const selected = useSelected(); - const focused = useFocused(); - - return ( - - {children} - - ); -}); +); diff --git a/packages/combobox/src/hooks/useComboboxInput.ts b/packages/combobox/src/hooks/useComboboxInput.ts index 7d69b26c8d..8db8843e1b 100644 --- a/packages/combobox/src/hooks/useComboboxInput.ts +++ b/packages/combobox/src/hooks/useComboboxInput.ts @@ -1,7 +1,14 @@ -import { HTMLAttributes, RefObject, useCallback, useEffect } from 'react'; +import { + HTMLAttributes, + RefObject, + useCallback, + useEffect, + useRef, +} from 'react'; import { findNodePath, focusEditor, + Hotkeys, isHotkey, removeNodes, useEditorRef, @@ -20,6 +27,7 @@ export interface UseComboboxInputOptions { cancelInputOnArrowLeftRight?: boolean; cancelInputOnDeselect?: boolean; cancelInputOnBlur?: boolean; + forwardUndoRedoToEditor?: boolean; onCancelInput?: (cause: CancelComboboxInputCause) => void; } @@ -41,6 +49,7 @@ export const useComboboxInput = ({ cancelInputOnArrowLeftRight = true, cancelInputOnDeselect = true, cancelInputOnBlur = true, + forwardUndoRedoToEditor = true, onCancelInput, }: UseComboboxInputOptions): UseComboboxInputResult => { const editor = useEditorRef(); @@ -81,10 +90,20 @@ export const useComboboxInput = ({ } }, [autoFocus, ref]); + /** + * Storing the previous selection lets us determine whether the input has + * been actively deselected. When undoing or redoing causes a combobox input + * to be inserted, selected can be temporarily false. Removing the input at + * this point is incorrect and crashes the editor. + */ + const previousSelected = useRef(selected); + useEffect(() => { - if (!selected && cancelInputOnDeselect) { + if (previousSelected.current && !selected && cancelInputOnDeselect) { cancelInput('deselect'); } + + previousSelected.current = selected; }, [selected, cancelInputOnDeselect, cancelInput]); return { @@ -119,6 +138,15 @@ export const useComboboxInput = ({ ) { cancelInput('arrowRight', true); } + + const isUndo = Hotkeys.isUndo(event) && editor.history.undos.length > 0; + const isRedo = Hotkeys.isRedo(event) && editor.history.redos.length > 0; + + if (forwardUndoRedoToEditor && (isUndo || isRedo)) { + event.preventDefault(); + editor[isUndo ? 'undo' : 'redo'](); + focusEditor(editor); + } }, onBlur: () => { if (cancelInputOnBlur) { diff --git a/packages/combobox/src/types.ts b/packages/combobox/src/types.ts index 9d1b13f764..7929bcc48d 100644 --- a/packages/combobox/src/types.ts +++ b/packages/combobox/src/types.ts @@ -1,10 +1,10 @@ import { PlateEditor, TElement } from '@udecode/plate-common'; export interface TriggerComboboxPlugin { - trigger?: string; + trigger?: string | string[] | RegExp; triggerPreviousCharPattern?: RegExp; triggerQuery?: (editor: PlateEditor) => boolean; - createComboboxInput?: () => TElement; + createComboboxInput?: (trigger: string) => TElement; } export type ComboboxInputCursorState = { diff --git a/packages/combobox/src/withTriggerCombobox.spec.tsx b/packages/combobox/src/withTriggerCombobox.spec.tsx new file mode 100644 index 0000000000..fc3c6dfe58 --- /dev/null +++ b/packages/combobox/src/withTriggerCombobox.spec.tsx @@ -0,0 +1,222 @@ +/** @jsx jsx */ + +import { + TriggerComboboxPlugin, + withTriggerCombobox, +} from '@udecode/plate-combobox'; +import { createPlateEditor, createPluginFactory } from '@udecode/plate-common'; +import { createParagraphPlugin } from '@udecode/plate-paragraph'; +import { jsx } from '@udecode/plate-test-utils'; + +const createExampleComboboxPlugin = createPluginFactory({ + key: 'exampleCombobox', + withOverrides: withTriggerCombobox, + plugins: [ + { + key: 'mention_input', + isElement: true, + isInline: true, + isVoid: true, + }, + ], +}); + +const plugins = [ + createParagraphPlugin(), + + createExampleComboboxPlugin({ + key: 'exampleCombobox1', + options: { + trigger: ['@', '#'], + triggerPreviousCharPattern: /^$|^[\s"']$/, + createComboboxInput: (trigger) => ({ + type: 'mention_input', + trigger, + children: [{ text: '' }], + }), + }, + }), + + createExampleComboboxPlugin({ + key: 'exampleCombobox2', + options: { + trigger: ':', + triggerPreviousCharPattern: /^\s?$/, + createComboboxInput: () => ({ + type: 'mention_input', + trigger: ':', + children: [{ text: '' }], + }), + }, + }), +]; + +const createEditorWithCombobox = (chidren: any) => + createPlateEditor({ + editor: ({chidren}) as any, + plugins, + }); + +jsx; + +describe('withTriggerCombobox', () => { + ['@', '#', ':'].forEach((trigger) => { + describe(`when typing "${trigger}"`, () => { + it('should insert a combobox input when the trigger is inserted between words', () => { + const editor = createEditorWithCombobox( + + hello world + + ); + + editor.insertText(trigger); + + expect(editor.children).toEqual([ + + hello + + + + + world + , + ]); + }); + + it('should insert a combobox input when the trigger is inserted at line beginning followed by a whitespace', () => { + const editor = createEditorWithCombobox( + + hello world + + ); + + editor.insertText(trigger); + + expect(editor.children).toEqual([ + + + + + + + hello world + , + ]); + }); + + it('should insert a combobox input when the trigger is inserted at line end preceded by a whitespace', () => { + const editor = createEditorWithCombobox( + + hello world + + ); + + editor.insertText(trigger); + + expect(editor.children).toEqual([ + + hello world + + + + + + , + ]); + }); + + it('should insert the trigger as text when the trigger is appended to a word', () => { + const editor = createEditorWithCombobox( + + hello + + + ); + + editor.insertText(trigger); + + expect(editor.children).toEqual([ + + hello{trigger} + + , + ]); + }); + + it('should insert a combobox input when the trigger is prepended to a word', () => { + const editor = createEditorWithCombobox( + + + hello + + ); + + editor.insertText(trigger); + + expect(editor.children).toEqual([ + + + + + + + hello + , + ]); + }); + + it('should insert the trigger as text when the trigger is inserted into a word', () => { + const editor = createEditorWithCombobox( + + hel + + lo + + ); + + editor.insertText(trigger); + + expect(editor.children).toEqual([ + + hel{trigger} + + lo + , + ]); + }); + }); + }); + + it('should insert text when not trigger', () => { + const editor = createEditorWithCombobox( + + + + ); + + editor.insertText('a'); + + expect(editor.children).toEqual([a]); + }); + + it('should insert a combobox input when the trigger is inserted after the specified pattern', () => { + const editor = createEditorWithCombobox( + + hello "" + + ); + + editor.insertText('@'); + + expect(editor.children).toEqual([ + + hello " + + + + + " + , + ]); + }); +}); diff --git a/packages/combobox/src/withTriggerCombobox.ts b/packages/combobox/src/withTriggerCombobox.ts index ef7ee5c36d..7916f6c5fb 100644 --- a/packages/combobox/src/withTriggerCombobox.ts +++ b/packages/combobox/src/withTriggerCombobox.ts @@ -27,10 +27,22 @@ export const withTriggerCombobox = < ) => { const { insertText } = editor; + const matchesTrigger = (text: string) => { + if (trigger instanceof RegExp) { + return trigger.test(text); + } + + if (Array.isArray(trigger)) { + return trigger.includes(text); + } + + return text === trigger; + }; + editor.insertText = (text) => { if ( !editor.selection || - text !== trigger || + !matchesTrigger(text) || (triggerQuery && !triggerQuery(editor as PlateEditor)) ) { return insertText(text); @@ -49,9 +61,9 @@ export const withTriggerCombobox = < const matchesPreviousCharPattern = triggerPreviousCharPattern?.test(previousChar); - if (matchesPreviousCharPattern && text === trigger) { + if (matchesPreviousCharPattern) { const inputNode: TElement = createComboboxInput - ? createComboboxInput() + ? createComboboxInput(text) : { type, children: [{ text: '' }] }; return editor.insertNode(inputNode); diff --git a/packages/mention/src/__tests__/createEditorWithMentions.tsx b/packages/mention/src/__tests__/createEditorWithMentions.tsx deleted file mode 100644 index f72a57d83f..0000000000 --- a/packages/mention/src/__tests__/createEditorWithMentions.tsx +++ /dev/null @@ -1,44 +0,0 @@ -/** @jsx jsx */ - -import { createPlateEditor, PlateEditor, Value } from '@udecode/plate-common'; -import { createParagraphPlugin } from '@udecode/plate-paragraph'; -import { jsx } from '@udecode/plate-test-utils'; - -import { createMentionPlugin } from '../createMentionPlugin'; - -jsx; - -export type CreateEditorOptions = { - multipleMentionPlugins?: boolean; - pluginOptions?: { - key?: string; - trigger?: string; - triggerPreviousCharPattern?: RegExp; - }; -}; - -export const createEditorWithMentions = ( - state: React.ReactElement, - { - multipleMentionPlugins, - pluginOptions: { trigger, key, triggerPreviousCharPattern } = {}, - }: CreateEditorOptions = {} -): PlateEditor => { - const plugins = [ - createParagraphPlugin(), - createMentionPlugin({ - key, - options: { trigger, triggerPreviousCharPattern }, - }), - ]; - if (multipleMentionPlugins) { - plugins.push( - createMentionPlugin({ key: 'mention2', options: { trigger: '#' } }) - ); - } - - return createPlateEditor({ - editor: ({state}) as any, - plugins, - }); -}; diff --git a/packages/mention/src/createMentionPlugin.ts b/packages/mention/src/createMentionPlugin.ts index 74c2111027..d0359c23ec 100644 --- a/packages/mention/src/createMentionPlugin.ts +++ b/packages/mention/src/createMentionPlugin.ts @@ -1,9 +1,7 @@ -import { createPluginFactory, removeNodes } from '@udecode/plate-common'; +import { withTriggerCombobox } from '@udecode/plate-combobox'; +import { createPluginFactory } from '@udecode/plate-common'; -import { mentionOnKeyDownHandler } from './handlers/mentionOnKeyDownHandler'; -import { isSelectionInMentionInput } from './queries/index'; import { MentionPlugin } from './types'; -import { withMention } from './withMention'; export const ELEMENT_MENTION = 'mention'; export const ELEMENT_MENTION_INPUT = 'mention_input'; @@ -17,20 +15,15 @@ export const createMentionPlugin = createPluginFactory({ isInline: true, isVoid: true, isMarkableVoid: true, - handlers: { - onKeyDown: mentionOnKeyDownHandler({ query: isSelectionInMentionInput }), - onBlur: (editor) => () => { - // remove mention_input nodes from editor on blur - removeNodes(editor, { - match: (n) => n.type === ELEMENT_MENTION_INPUT, - at: [], - }); - }, - }, - withOverrides: withMention, + withOverrides: withTriggerCombobox, options: { trigger: '@', triggerPreviousCharPattern: /^\s?$/, + createComboboxInput: (trigger) => ({ + type: ELEMENT_MENTION_INPUT, + trigger, + children: [{ text: '' }], + }), createMentionNode: (item) => ({ value: item.text }), }, plugins: [ @@ -38,11 +31,7 @@ export const createMentionPlugin = createPluginFactory({ key: ELEMENT_MENTION_INPUT, isElement: true, isInline: true, + isVoid: true, }, ], - then: (editor, { key }) => ({ - options: { - id: key, - }, - }), }); diff --git a/packages/mention/src/getMentionOnSelectItem.ts b/packages/mention/src/getMentionOnSelectItem.ts index b4b6006c0b..1e1a3f4750 100644 --- a/packages/mention/src/getMentionOnSelectItem.ts +++ b/packages/mention/src/getMentionOnSelectItem.ts @@ -1,11 +1,3 @@ -import { - comboboxActions, - ComboboxOnSelectItem, - comboboxSelectors, - Data, - NoData, - TComboboxItem, -} from '@udecode/plate-combobox'; import { getBlockAbove, getPlugin, @@ -13,77 +5,51 @@ import { insertText, isEndPoint, moveSelection, + PlateEditor, PlatePluginKey, - removeNodes, - select, - TNodeProps, - withoutMergingHistory, - withoutNormalizing, + Value, } from '@udecode/plate-common'; import { ELEMENT_MENTION } from './createMentionPlugin'; -import { isNodeMentionInput } from './queries/isNodeMentionInput'; -import { MentionPlugin, TMentionElement } from './types'; - -export interface CreateMentionNode { - ( - item: TComboboxItem, - meta: CreateMentionNodeMeta - ): TNodeProps; -} +import { MentionPlugin, TMentionElement, TMentionItemBase } from './types'; -export interface CreateMentionNodeMeta { - search: string; -} +export type MentionOnSelectItem< + TItem extends TMentionItemBase = TMentionItemBase, +> = ( + editor: PlateEditor, + item: TItem, + search?: string +) => void; export const getMentionOnSelectItem = - ({ + ({ key = ELEMENT_MENTION, - }: PlatePluginKey = {}): ComboboxOnSelectItem => - (editor, item) => { - const targetRange = comboboxSelectors.targetRange(); - if (!targetRange) return; - + }: PlatePluginKey = {}): MentionOnSelectItem => + (editor, item, search = '') => { const { type, options: { insertSpaceAfterMention, createMentionNode }, } = getPlugin(editor as any, key); - const pathAbove = getBlockAbove(editor)?.[1]; - const isBlockEnd = () => - editor.selection && - pathAbove && - isEndPoint(editor, editor.selection.anchor, pathAbove); - - withoutNormalizing(editor, () => { - // Selectors are sensitive to operations, it's better to create everything - // before the editor state is changed. For example, asking for text after - // removeNodes below will return null. - const props = createMentionNode!(item, { - search: comboboxSelectors.text() ?? '', - }); - - select(editor, targetRange); + const props = createMentionNode!(item, search); - withoutMergingHistory(editor, () => - removeNodes(editor, { - match: (node) => isNodeMentionInput(editor, node), - }) - ); + insertNodes(editor, { + type, + children: [{ text: '' }], + ...props, + } as TMentionElement); - insertNodes(editor, { - type, - children: [{ text: '' }], - ...props, - } as TMentionElement); + // move the selection after the element + moveSelection(editor, { unit: 'offset' }); - // move the selection after the element - moveSelection(editor, { unit: 'offset' }); + const pathAbove = getBlockAbove(editor)?.[1]; - if (isBlockEnd() && insertSpaceAfterMention) { - insertText(editor, ' '); - } - }); + const isBlockEnd = + editor.selection && + pathAbove && + isEndPoint(editor, editor.selection.anchor, pathAbove); - return comboboxActions.reset(); + if (isBlockEnd && insertSpaceAfterMention) { + insertText(editor, ' '); + } }; diff --git a/packages/mention/src/handlers/index.ts b/packages/mention/src/handlers/index.ts deleted file mode 100644 index 60fbc656c4..0000000000 --- a/packages/mention/src/handlers/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** - * @file Automatically generated by barrelsby. - */ - -export * from './mentionOnKeyDownHandler'; diff --git a/packages/mention/src/handlers/mentionOnKeyDownHandler.spec.tsx b/packages/mention/src/handlers/mentionOnKeyDownHandler.spec.tsx deleted file mode 100644 index 71962101b0..0000000000 --- a/packages/mention/src/handlers/mentionOnKeyDownHandler.spec.tsx +++ /dev/null @@ -1,34 +0,0 @@ -/** @jsx jsx */ - -import * as isHotkey from '@udecode/plate-core'; -import { jsx } from '@udecode/plate-test-utils'; - -import { createEditorWithMentions } from '../__tests__/createEditorWithMentions'; - -jsx; - -describe('mentionOnKeyDownHandler', () => { - const trigger = '@'; - - it('should remove the input on escape', () => { - const editor = createEditorWithMentions( - - - - - - - , - { pluginOptions: { trigger } } - ); - - jest.spyOn(isHotkey, 'isHotkey').mockReturnValue(true); - - // mentionOnKeyDownHandler({})(editor)( - // new KeyboardEvent('keydown', { key: 'Escape' }) as any - // ); - - // expect(editor.children).toEqual([@]); - expect(editor.children).toEqual(editor.children); - }); -}); diff --git a/packages/mention/src/handlers/mentionOnKeyDownHandler.ts b/packages/mention/src/handlers/mentionOnKeyDownHandler.ts deleted file mode 100644 index be667cf69f..0000000000 --- a/packages/mention/src/handlers/mentionOnKeyDownHandler.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { - isHotkey, - KeyboardEventHandler, - moveSelection, - moveSelectionByOffset, - MoveSelectionByOffsetOptions, - PlateEditor, - Value, -} from '@udecode/plate-common'; - -import { findMentionInput } from '../queries/index'; -import { removeMentionInput } from '../transforms/index'; - -export const mentionOnKeyDownHandler: ( - options?: MoveSelectionByOffsetOptions -) => (editor: PlateEditor) => KeyboardEventHandler = - (options) => (editor) => (event) => { - if (isHotkey('escape', event)) { - const currentMentionInput = findMentionInput(editor)!; - if (currentMentionInput) { - event.preventDefault(); - removeMentionInput(editor, currentMentionInput[1]); - moveSelection(editor, { unit: 'word' }); - return true; - } - return false; - } - - return moveSelectionByOffset(editor, options)(event); - }; diff --git a/packages/mention/src/index.ts b/packages/mention/src/index.ts index d1612be983..cf82b60840 100644 --- a/packages/mention/src/index.ts +++ b/packages/mention/src/index.ts @@ -5,7 +5,3 @@ export * from './createMentionPlugin'; export * from './getMentionOnSelectItem'; export * from './types'; -export * from './withMention'; -export * from './handlers/index'; -export * from './queries/index'; -export * from './transforms/index'; diff --git a/packages/mention/src/queries/findMentionInput.ts b/packages/mention/src/queries/findMentionInput.ts deleted file mode 100644 index bef2ae5734..0000000000 --- a/packages/mention/src/queries/findMentionInput.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { - findNode, - FindNodeOptions, - getPluginType, - PlateEditor, - Value, -} from '@udecode/plate-common'; - -import { ELEMENT_MENTION_INPUT } from '../createMentionPlugin'; -import { TMentionInputElement } from '../types'; - -export const findMentionInput = ( - editor: PlateEditor, - options?: Omit, 'match'> -) => - findNode(editor, { - ...options, - match: { type: getPluginType(editor, ELEMENT_MENTION_INPUT) }, - }); diff --git a/packages/mention/src/queries/index.ts b/packages/mention/src/queries/index.ts deleted file mode 100644 index 94a16a3a1d..0000000000 --- a/packages/mention/src/queries/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * @file Automatically generated by barrelsby. - */ - -export * from './findMentionInput'; -export * from './isNodeMentionInput'; -export * from './isSelectionInMentionInput'; diff --git a/packages/mention/src/queries/isNodeMentionInput.ts b/packages/mention/src/queries/isNodeMentionInput.ts deleted file mode 100644 index 70dd31baf8..0000000000 --- a/packages/mention/src/queries/isNodeMentionInput.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { - getPluginType, - PlateEditor, - TNode, - Value, -} from '@udecode/plate-common'; - -import { ELEMENT_MENTION_INPUT } from '../createMentionPlugin'; -import { TMentionInputElement } from '../types'; - -export const isNodeMentionInput = ( - editor: PlateEditor, - node: TNode -): node is TMentionInputElement => { - return node.type === getPluginType(editor, ELEMENT_MENTION_INPUT); -}; diff --git a/packages/mention/src/queries/isSelectionInMentionInput.ts b/packages/mention/src/queries/isSelectionInMentionInput.ts deleted file mode 100644 index 87a5b96ec3..0000000000 --- a/packages/mention/src/queries/isSelectionInMentionInput.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { PlateEditor, Value } from '@udecode/plate-common'; - -import { findMentionInput } from './findMentionInput'; - -export const isSelectionInMentionInput = ( - editor: PlateEditor -) => findMentionInput(editor) !== undefined; diff --git a/packages/mention/src/transforms/index.ts b/packages/mention/src/transforms/index.ts deleted file mode 100644 index ad93e4898f..0000000000 --- a/packages/mention/src/transforms/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** - * @file Automatically generated by barrelsby. - */ - -export * from './removeMentionInput'; diff --git a/packages/mention/src/transforms/removeMentionInput.ts b/packages/mention/src/transforms/removeMentionInput.ts deleted file mode 100644 index ebd79ec96a..0000000000 --- a/packages/mention/src/transforms/removeMentionInput.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { - EText, - getNode, - getNodeString, - PlateEditor, - replaceNode, - Value, - withoutNormalizing, -} from '@udecode/plate-common'; -import { Path } from 'slate'; - -import { TMentionInputElement } from '../types'; - -export const removeMentionInput = ( - editor: PlateEditor, - path: Path -) => - withoutNormalizing(editor, () => { - const node = getNode(editor, path); - if (!node) return; - - const { trigger } = node; - - const text = getNodeString(node); - - replaceNode(editor, { - at: path, - nodes: { text: `${trigger}${text}` } as EText, - }); - }); diff --git a/packages/mention/src/types.ts b/packages/mention/src/types.ts index fa2dd0a5f7..07e596e8e1 100644 --- a/packages/mention/src/types.ts +++ b/packages/mention/src/types.ts @@ -1,22 +1,24 @@ -import { Data, NoData } from '@udecode/plate-combobox'; -import { PlateEditor, TElement } from '@udecode/plate-common'; +import { TriggerComboboxPlugin } from '@udecode/plate-combobox'; +import { TElement, TNodeProps } from '@udecode/plate-common'; -import { CreateMentionNode } from './getMentionOnSelectItem'; - -export interface TMentionElement extends TElement { - value: string; +export interface TMentionItemBase { + text: string; } export interface TMentionInputElement extends TElement { trigger: string; } -export interface MentionPlugin { - createMentionNode?: CreateMentionNode; - id?: string; +export interface TMentionElement extends TElement { + value: string; +} + +export interface MentionPlugin< + TItem extends TMentionItemBase = TMentionItemBase, +> extends TriggerComboboxPlugin { + createMentionNode?: ( + item: TItem, + search: string + ) => TNodeProps; insertSpaceAfterMention?: boolean; - trigger?: string; - triggerPreviousCharPattern?: RegExp; - inputCreation?: { key: string; value: string }; - query?: (editor: PlateEditor) => boolean; } diff --git a/packages/mention/src/withMention.spec.tsx b/packages/mention/src/withMention.spec.tsx deleted file mode 100644 index d3fec9face..0000000000 --- a/packages/mention/src/withMention.spec.tsx +++ /dev/null @@ -1,636 +0,0 @@ -/** @jsx jsx */ - -import { - comboboxActions, - comboboxSelectors, - ComboboxState, -} from '@udecode/plate-combobox'; -import { - moveSelection, - PlateEditor, - select, - Value, -} from '@udecode/plate-common'; -import { - createDataTransfer, - DataTransferDataMap, - jsx, -} from '@udecode/plate-test-utils'; -import { Range } from 'slate'; - -import { createEditorWithMentions } from './__tests__/createEditorWithMentions'; -import { getMentionOnSelectItem } from './getMentionOnSelectItem'; - -jsx; - -describe('withMention', () => { - const trigger = '@'; - const key = 'mention'; - - type CreateEditorOptions = { - multipleMentionPlugins?: boolean; - triggerPreviousCharPattern?: RegExp; - }; - - const createEditor = ( - state: React.ReactElement, - options: CreateEditorOptions = {} - ): PlateEditor => - createEditorWithMentions(state, { - ...options, - pluginOptions: { - ...options, - key, - trigger, - }, - }); - - const createEditorWithMentionInput = ( - at: React.ReactElement = ( - - - - - ), - options?: CreateEditorOptions - ): PlateEditor => { - const editor = createEditor(at, options) as PlateEditor; - - editor.insertText(trigger); - - return editor; - }; - - beforeEach(() => { - comboboxActions.byId({}); - }); - - describe('creating a mention input', () => { - it('should insert a mention input when the trigger is inserted between words', () => { - const editor = createEditorWithMentionInput( - - hello world - - ); - - expect(editor.children).toEqual([ - - hello - - - - - world - , - ]); - }); - - it('should insert a mention input when the trigger is inserted at line beginning followed by a whitespace', () => { - const editor = createEditorWithMentionInput( - - hello world - - ); - - expect(editor.children).toEqual([ - - - - - - - hello world - , - ]); - }); - - it('should insert a mention input when the trigger is inserted at line end preceded by a whitespace', () => { - const editor = createEditorWithMentionInput( - - hello world - - ); - - expect(editor.children).toEqual([ - - hello world - - - - - - , - ]); - }); - - it('should insert the trigger as text when the trigger is appended to a word', () => { - const editor = createEditor( - - hello - - - ); - - editor.insertText(trigger); - - expect(editor.children).toEqual([ - - hello@ - - , - ]); - }); - - it('should insert the trigger as text when the trigger is prepended to a word', () => { - const editor = createEditor( - - - hello - - ); - - editor.insertText(trigger); - - expect(editor.children).toEqual([ - - - - - - - hello - , - ]); - }); - - it('should insert the trigger as text when the trigger is inserted into a word', () => { - const editor = createEditor( - - hel - - lo - - ); - - editor.insertText(trigger); - - expect(editor.children).toEqual([ - - hel@ - - lo - , - ]); - }); - - it('should insert text when not trigger', () => { - const editor = createEditor( - - - - ); - - editor.insertText('a'); - - expect(editor.children).toEqual([a]); - }); - - it('should insert a mention input when the trigger is inserted after the specified pattern', () => { - const emptyOrSpaceOrQuotePattern = /^$|^[\s"']$/; - const editor = createEditor( - - hello "" - , - { - triggerPreviousCharPattern: emptyOrSpaceOrQuotePattern, - } - ); - - editor.insertText(trigger); - - expect(editor.children).toEqual([ - - hello " - - - - - " - , - ]); - }); - }); - - describe('removing a mention input', () => { - it('should remove the mention input when the selection is removed from it', () => { - const editor = createEditor( - - - - - - - - - ); - - select(editor, { - path: [0, 2], - offset: 0, - }); - - expect(editor.children).toEqual([@]); - }); - - it('should preserve the text that was typed into the mention input after removing', () => { - const editor = createEditor( - - - - hello - - - - - ); - - select(editor, { - path: [0, 2], - offset: 0, - }); - - expect(editor.children).toEqual([@hello]); - }); - - it('should change the selection to the requested location', () => { - const editor = createEditor( - - - - hello - - - - - ); - - select(editor, { - path: [0, 2], - offset: 0, - }); - - expect(editor.selection).toEqual({ - anchor: { path: [0, 0], offset: 6 }, - focus: { path: [0, 0], offset: 6 }, - }); - }); - - it('should remove the input when deleting backward in empty input', () => { - const editor = createEditor( - - - - - - - - ); - - editor.deleteBackward('character'); - - expect(editor.children).toEqual([@]); - - expect(editor.selection).toEqual({ - anchor: { path: [0, 0], offset: 1 }, - focus: { path: [0, 0], offset: 1 }, - }); - }); - - it('should block insert break', () => { - const editor = createEditor( - - - - n - - - - - ); - - editor.insertBreak(); - - expect(editor.children).toEqual([ - - - - n - - - - , - ]); - - expect(editor.selection).toEqual({ - anchor: { path: [0, 1, 0], offset: 1 }, - focus: { path: [0, 1, 0], offset: 1 }, - }); - }); - }); - - describe('typing in a mention input', () => { - // TODO: remove if slate upgrade handles - it('should type into a mention input if the selection is in it', () => { - const editor = createEditorWithMentionInput( - - - - - ); - - editor.insertText('a'); - - expect(editor.children).toEqual([ - - - a - - , - ]); - }); - - it('should type the trigger as text when inside a mention input', () => { - const editor = createEditorWithMentionInput( - - - - ); - - editor.insertText(trigger); - - expect(editor.children).toEqual([ - - - {trigger} - - , - ]); - }); - }); - - describe('history', () => { - it('should undo inserting a mention by showing mention input', async () => { - const editor = createEditorWithMentionInput( - - - hello world - - - ); - - // flush previous ops to get a new undo batch going for mention input - await Promise.resolve(); - - getMentionOnSelectItem()(editor, { key: 'test', text: 'test' }); - - editor.undo(); - - expect(editor.children).toEqual([ - - hello - - - - world - , - ]); - - editor.undo(); - - expect(editor.children).toEqual([ - - - hello world - - , - ]); - }); - - it('should undo inserting a mention after input by showing mention input with the text', async () => { - const editor = createEditorWithMentionInput( - - - hello world - - - ); - - // flush previous ops to get a new undo batch going for mention input - await Promise.resolve(); - - editor.insertText('t'); - editor.insertText('e'); - - // flush previous ops to get a new undo batch going for mention input - await Promise.resolve(); - - getMentionOnSelectItem()(editor, { key: 'test', text: 'test' }); - - editor.undo(); - - expect(editor.children).toEqual([ - - hello - - te - - world - , - ]); - - editor.undo(); - - expect(editor.children).toEqual([ - - hello - - - - world - , - ]); - - editor.undo(); - - expect(editor.children).toEqual([ - - - hello world - - , - ]); - }); - }); - - describe('combobox', () => { - it('should show the combobox when a mention input is created', () => { - createEditorWithMentionInput( - - - , - { multipleMentionPlugins: true } - ); - - expect(comboboxSelectors.state()).toMatchObject>({ - activeId: key, - }); - }); - - it('should close the combobox when a mention input is removed', () => { - const editor = createEditorWithMentionInput( - - - - - ); - - select(editor, { - path: [0, 2], - offset: 0, - }); - - expect(comboboxSelectors.state()).toMatchObject>({ - activeId: null, - }); - }); - - it('should update the text in the combobox when typing', () => { - const editor = createEditorWithMentionInput(); - - editor.insertText('abc'); - expect(comboboxSelectors.state()).toMatchObject>({ - text: 'abc', - }); - - editor.deleteBackward('character'); - expect(comboboxSelectors.state()).toMatchObject>({ - text: 'ab', - }); - - moveSelection(editor, { distance: 1, reverse: true }); - editor.deleteForward('character'); - expect(comboboxSelectors.state()).toMatchObject>({ - text: 'a', - }); - }); - }); - - describe('paste', () => { - const testPaste: ( - data: DataTransferDataMap, - input: React.ReactElement, - expected: React.ReactElement - ) => void = (data, input, expected) => { - const editor = createEditorWithMentionInput(input); - - editor.insertData(createDataTransfer(data)); - - expect(editor.children).toEqual([expected]); - }; - - const testPasteBasic: ( - data: DataTransferDataMap, - expected: string - ) => void = (data, expected) => { - testPaste( - data, - - - , - - - {expected} - - - ); - }; - - type PasteTestCase = { - data: DataTransferDataMap; - expected: string; - }; - - const basePasteTestSuite = ({ - simple, - whitespace, - newLine, - newLineAndWhitespace, - }: { - simple: PasteTestCase; - whitespace: PasteTestCase; - newLine: PasteTestCase; - newLineAndWhitespace: PasteTestCase; - }): void => { - it('should paste the clipboard contents into mention as text', () => - testPasteBasic(simple.data, simple.expected)); - - it('should merge lines', () => - testPasteBasic(newLine.data, newLine.expected)); - - it('should trim the text', () => - testPasteBasic(whitespace.data, whitespace.expected)); - - it('should trim every line before merging', () => - testPasteBasic( - newLineAndWhitespace.data, - newLineAndWhitespace.expected - )); - }; - - describe('html', () => { - basePasteTestSuite({ - simple: { - data: new Map([['text/html', 'hello']]), - expected: 'hello', - }, - whitespace: { - data: new Map([['text/html', ' hello ']]), - expected: 'hello', - }, - newLine: { - data: new Map([ - ['text/html', 'hello
world'], - ]), - expected: 'helloworld', - }, - newLineAndWhitespace: { - data: new Map([ - ['text/html', ' hello
world '], - ]), - expected: 'helloworld', - }, - }); - }); - - describe('plain text', () => { - basePasteTestSuite({ - simple: { - data: new Map([['text/plain', 'hello']]), - expected: 'hello', - }, - whitespace: { - data: new Map([['text/plain', ' hello ']]), - expected: 'hello', - }, - newLine: { - data: new Map([['text/plain', 'hello\r\nworld\n!\r!']]), - expected: 'helloworld!!', - }, - newLineAndWhitespace: { - data: new Map([['text/plain', ' hello \r\n world \n ! \r ! ']]), - expected: 'helloworld!!', - }, - }); - }); - }); -}); diff --git a/packages/mention/src/withMention.ts b/packages/mention/src/withMention.ts deleted file mode 100644 index f0917cb9a6..0000000000 --- a/packages/mention/src/withMention.ts +++ /dev/null @@ -1,205 +0,0 @@ -import { comboboxActions } from '@udecode/plate-combobox'; -import { - getEditorString, - getNodeString, - getPlugin, - getPointBefore, - getRange, - moveSelection, - PlateEditor, - setSelection, - TNode, - TText, - Value, - WithPlatePlugin, -} from '@udecode/plate-common'; -import { Range } from 'slate'; - -import { ELEMENT_MENTION_INPUT } from './createMentionPlugin'; -import { - findMentionInput, - isNodeMentionInput, - isSelectionInMentionInput, -} from './queries/index'; -import { removeMentionInput } from './transforms/removeMentionInput'; -import { MentionPlugin, TMentionInputElement } from './types'; - -export const withMention = < - V extends Value = Value, - E extends PlateEditor = PlateEditor, ->( - editor: E, - { - options: { id, trigger, triggerPreviousCharPattern, query, inputCreation }, - }: WithPlatePlugin -) => { - const { type } = getPlugin<{}, V>(editor, ELEMENT_MENTION_INPUT); - - const { - apply, - insertBreak, - insertText, - deleteBackward, - insertFragment, - insertTextData, - insertNode, - } = editor; - - const stripNewLineAndTrim: (text: string) => string = (text) => { - return text - .split(/\r\n|\r|\n/) - .map((line) => line.trim()) - .join(''); - }; - - editor.insertFragment = (fragment) => { - const inMentionInput = findMentionInput(editor) !== undefined; - if (!inMentionInput) { - return insertFragment(fragment); - } - - return insertText( - fragment.map((node) => stripNewLineAndTrim(getNodeString(node))).join('') - ); - }; - - editor.insertTextData = (data) => { - const inMentionInput = findMentionInput(editor) !== undefined; - if (!inMentionInput) { - return insertTextData(data); - } - - const text = data.getData('text/plain'); - if (!text) { - return false; - } - - editor.insertText(stripNewLineAndTrim(text)); - - return true; - }; - - editor.deleteBackward = (unit) => { - const currentMentionInput = findMentionInput(editor); - if (currentMentionInput && getNodeString(currentMentionInput[0]) === '') { - removeMentionInput(editor, currentMentionInput[1]); - return moveSelection(editor, { unit: 'word' }); - } - - deleteBackward(unit); - }; - - editor.insertBreak = () => { - if (isSelectionInMentionInput(editor)) { - return; - } - - insertBreak(); - }; - - editor.insertText = (text) => { - if ( - !editor.selection || - text !== trigger || - (query && !query(editor as PlateEditor)) || - isSelectionInMentionInput(editor) - ) { - return insertText(text); - } - - // Make sure a mention input is created at the beginning of line or after a whitespace - const previousChar = getEditorString( - editor, - getRange( - editor, - editor.selection, - getPointBefore(editor, editor.selection) - ) - ); - const matchesPreviousCharPattern = - triggerPreviousCharPattern?.test(previousChar); - - if (matchesPreviousCharPattern && text === trigger) { - const data: TMentionInputElement = { - type, - children: [{ text: '' }], - trigger, - }; - if (inputCreation) { - data[inputCreation.key] = inputCreation.value; - } - return insertNode(data); - } - - return insertText(text); - }; - - editor.apply = (operation) => { - apply(operation); - - if (operation.type === 'insert_text' || operation.type === 'remove_text') { - const currentMentionInput = findMentionInput(editor); - if (currentMentionInput) { - comboboxActions.text(getNodeString(currentMentionInput[0])); - } - } else if (operation.type === 'set_selection') { - const previousMentionInputPath = Range.isRange(operation.properties) - ? findMentionInput(editor, { at: operation.properties })?.[1] - : undefined; - - const currentMentionInputPath = Range.isRange(operation.newProperties) - ? findMentionInput(editor, { at: operation.newProperties })?.[1] - : undefined; - - if (previousMentionInputPath && !currentMentionInputPath) { - removeMentionInput(editor, previousMentionInputPath); - moveSelection(editor, { unit: 'word' }); - } - - if (currentMentionInputPath) { - comboboxActions.targetRange(editor.selection); - } - } else if ( - operation.type === 'insert_node' && - isNodeMentionInput(editor, operation.node as TNode) - ) { - if ((operation.node as TMentionInputElement).trigger !== trigger) { - return; - } - - const text = - ((operation.node as TMentionInputElement).children as TText[])[0] - ?.text ?? ''; - - if ( - inputCreation === undefined || - operation.node[inputCreation.key] === inputCreation.value - ) { - // Needed for undo - after an undo a mention insert we only receive - // an insert_node with the mention input, i.e. nothing indicating that it - // was an undo. - setSelection(editor, { - anchor: { path: operation.path.concat([0]), offset: text.length }, - focus: { path: operation.path.concat([0]), offset: text.length }, - }); - - comboboxActions.open({ - activeId: id!, - text, - targetRange: editor.selection, - }); - } - } else if ( - operation.type === 'remove_node' && - isNodeMentionInput(editor, operation.node as TNode) - ) { - if ((operation.node as TMentionInputElement).trigger !== trigger) { - return; - } - - comboboxActions.reset(); - } - }; - - return editor; -}; diff --git a/packages/slate/src/interfaces/element/TElement.ts b/packages/slate/src/interfaces/element/TElement.ts index 3b07c30ab1..c801963638 100644 --- a/packages/slate/src/interfaces/element/TElement.ts +++ b/packages/slate/src/interfaces/element/TElement.ts @@ -37,12 +37,14 @@ export type EElementOrText = EElement | EText; export type ElementOf = TEditor extends N ? TElement : TElement extends N - ? TElement - : N extends TEditor - ? Extract | ElementOf - : N extends TElement - ? - | N - | Extract - | ElementOf - : never; + ? TElement + : N extends TEditor + ? + | Extract + | ElementOf + : N extends TElement + ? + | N + | Extract + | ElementOf + : never; diff --git a/packages/slate/src/interfaces/node/TAncestor.ts b/packages/slate/src/interfaces/node/TAncestor.ts index ba0f402863..6cde17aef9 100644 --- a/packages/slate/src/interfaces/node/TAncestor.ts +++ b/packages/slate/src/interfaces/node/TAncestor.ts @@ -20,9 +20,9 @@ export type EAncestor = AncestorOf>; export type AncestorOf = TEditor extends N ? TEditor | TElement : TElement extends N - ? TElement - : N extends TEditor - ? N | N['children'][number] | ElementOf - : N extends TElement - ? N | ElementOf - : never; + ? TElement + : N extends TEditor + ? N | N['children'][number] | ElementOf + : N extends TElement + ? N | ElementOf + : never; diff --git a/packages/slate/src/interfaces/node/TDescendant.ts b/packages/slate/src/interfaces/node/TDescendant.ts index 94da20dc5a..e4e5b29d3c 100644 --- a/packages/slate/src/interfaces/node/TDescendant.ts +++ b/packages/slate/src/interfaces/node/TDescendant.ts @@ -23,8 +23,8 @@ export type EDescendant = DescendantOf>; export type DescendantOf = N extends TEditor ? ElementOf | TextOf : N extends TElement - ? ElementOf | TextOf - : never; + ? ElementOf | TextOf + : never; /** * A utility type to get the child node types from a root node type. @@ -35,8 +35,8 @@ export type ChildOf< > = N extends TEditor ? N['children'][I] : N extends TElement - ? N['children'][I] - : never; + ? N['children'][I] + : never; export const isDescendant: (value: any) => value is TDescendant = (( node: any diff --git a/packages/slate/src/interfaces/node/TNode.ts b/packages/slate/src/interfaces/node/TNode.ts index 8f56b3736f..9a0016ad76 100644 --- a/packages/slate/src/interfaces/node/TNode.ts +++ b/packages/slate/src/interfaces/node/TNode.ts @@ -22,8 +22,8 @@ export type NodeOf = N | ElementOf | TextOf; export type TNodeProps = N extends TEditor ? Omit : N extends TElement - ? Omit - : Omit; + ? Omit + : Omit; /** * A helper type for narrowing matched nodes with a predicate. diff --git a/packages/slate/src/interfaces/text/TText.ts b/packages/slate/src/interfaces/text/TText.ts index 0e596a1845..48544fa616 100644 --- a/packages/slate/src/interfaces/text/TText.ts +++ b/packages/slate/src/interfaces/text/TText.ts @@ -24,14 +24,14 @@ export type EText = TextOf>; export type TextOf = TEditor extends N ? TText : TElement extends N - ? TText - : N extends TEditor - ? TextOf - : N extends TElement - ? Extract | TextOf - : N extends TText - ? N - : never; + ? TText + : N extends TEditor + ? TextOf + : N extends TElement + ? Extract | TextOf + : N extends TText + ? N + : never; /** * A utility type to get all the mark types from a root node type. @@ -42,6 +42,5 @@ export type MarksOf = Simplify< export type EMarks = MarksOf>; -export type MarkKeysOf = {} extends MarksOf - ? unknown - : keyof MarksOf; +export type MarkKeysOf = + {} extends MarksOf ? unknown : keyof MarksOf; From d958373355d0a93272a793ea113570a646eea1c4 Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Fri, 3 May 2024 16:45:16 +0100 Subject: [PATCH 27/63] Remove mention-combobox from registry.ts --- apps/www/src/registry/registry.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/apps/www/src/registry/registry.ts b/apps/www/src/registry/registry.ts index c3856cf493..0dfb9ec5a5 100644 --- a/apps/www/src/registry/registry.ts +++ b/apps/www/src/registry/registry.ts @@ -415,13 +415,6 @@ const ui: Registry = [ registryDependencies: ['toolbar'], files: ['plate-ui/media-toolbar-button.tsx'], }, - { - name: 'mention-combobox', - type: 'components:plate-ui', - dependencies: ['@udecode/plate-mention', '@udecode/plate-combobox'], - registryDependencies: ['combobox'], - files: ['plate-ui/mention-combobox.tsx'], - }, { name: 'mention-element', type: 'components:plate-ui', From 2aa6b5bdbff511a87bdf9304258de711374b32c4 Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Fri, 3 May 2024 16:45:29 +0100 Subject: [PATCH 28/63] yarn build:registry --- apps/www/public/registry/index.json | 14 -------------- .../styles/default/mention-input-element.json | 2 +- apps/www/src/__registry__/index.tsx | 7 ------- 3 files changed, 1 insertion(+), 22 deletions(-) diff --git a/apps/www/public/registry/index.json b/apps/www/public/registry/index.json index 925090b20e..a537fc72ef 100644 --- a/apps/www/public/registry/index.json +++ b/apps/www/public/registry/index.json @@ -636,20 +636,6 @@ ], "type": "components:plate-ui" }, - { - "name": "mention-combobox", - "dependencies": [ - "@udecode/plate-mention", - "@udecode/plate-combobox" - ], - "registryDependencies": [ - "combobox" - ], - "files": [ - "plate-ui/mention-combobox.tsx" - ], - "type": "components:plate-ui" - }, { "name": "mention-element", "dependencies": [ diff --git a/apps/www/public/registry/styles/default/mention-input-element.json b/apps/www/public/registry/styles/default/mention-input-element.json index 8747c8f0b9..aadfafb02c 100644 --- a/apps/www/public/registry/styles/default/mention-input-element.json +++ b/apps/www/public/registry/styles/default/mention-input-element.json @@ -7,7 +7,7 @@ "files": [ { "name": "mention-input-element.tsx", - "content": "import React from 'react';\nimport { cn, withRef } from '@udecode/cn';\nimport { getHandler, PlateElement } from '@udecode/plate-common';\nimport { useFocused, useSelected } from 'slate-react';\n\nexport const MentionInputElement = withRef<\n typeof PlateElement,\n {\n onClick?: (mentionNode: any) => void;\n }\n>(({ className, onClick, ...props }, ref) => {\n const { children, element } = props;\n\n const selected = useSelected();\n const focused = useFocused();\n\n return (\n \n {children}\n
\n );\n});\n" + "content": "import React, { useState } from 'react';\nimport { cn, withRef } from '@udecode/cn';\nimport { PlateElement } from '@udecode/plate-common';\nimport { getMentionOnSelectItem } from '@udecode/plate-mention';\n\nimport { MENTIONABLES } from '@/lib/plate/demo/values/mentionables';\n\nimport {\n InlineCombobox,\n InlineComboboxContent,\n InlineComboboxEmpty,\n InlineComboboxInput,\n InlineComboboxItem,\n} from './inline-combobox';\n\nconst onSelectItem = getMentionOnSelectItem();\n\nexport const MentionInputElement = withRef(\n ({ className, ...props }, ref) => {\n const { children, element, editor } = props;\n const [search, setSearch] = useState('');\n\n return (\n \n \n \n \n \n\n \n No results found\n\n {MENTIONABLES.map((item) => (\n onSelectItem(editor, item, search)}\n >\n {item.text}\n \n ))}\n \n \n\n {children}\n
\n );\n }\n);\n" } ], "type": "components:plate-ui" diff --git a/apps/www/src/__registry__/index.tsx b/apps/www/src/__registry__/index.tsx index 906a875247..ec10a10a2b 100644 --- a/apps/www/src/__registry__/index.tsx +++ b/apps/www/src/__registry__/index.tsx @@ -543,13 +543,6 @@ export const Index: Record = { files: ['registry/default/plate-ui/media-toolbar-button.tsx'], component: React.lazy(() => import('@/registry/default/plate-ui/media-toolbar-button')), }, - 'mention-combobox': { - name: 'mention-combobox', - type: 'components:plate-ui', - registryDependencies: ["combobox"], - files: ['registry/default/plate-ui/mention-combobox.tsx'], - component: React.lazy(() => import('@/registry/default/plate-ui/mention-combobox')), - }, 'mention-element': { name: 'mention-element', type: 'components:plate-ui', From d32625b04700109f47d23cdd463299326a597854 Mon Sep 17 00:00:00 2001 From: zbeyens Date: Mon, 6 May 2024 20:11:24 +0200 Subject: [PATCH 29/63] eslint --- .../src/lib/plate/demo/values/mentionables.ts | 2 +- .../default/example/playground-demo.tsx | 120 ++-- .../default/plate-ui/inline-combobox.tsx | 106 ++-- .../plate-ui/mention-input-element.tsx | 11 +- .../default/plate-ui/slash-input-element.tsx | 29 +- apps/www/src/registry/registry.ts | 538 +++++++++--------- .../combobox/src/hooks/useComboboxInput.ts | 63 +- .../src/hooks/useHTMLInputCursorState.ts | 11 +- .../legacy-combobox-delete-me/hooks/index.ts | 4 +- .../src/legacy-combobox-delete-me/index.ts | 4 +- .../legacy-combobox-delete-me/utils/index.ts | 4 +- packages/combobox/src/types.ts | 16 +- .../combobox/src/utils/filterWords.spec.ts | 2 +- packages/combobox/src/utils/filterWords.ts | 4 +- .../combobox/src/withTriggerCombobox.spec.tsx | 22 +- packages/combobox/src/withTriggerCombobox.ts | 17 +- packages/mention/src/createMentionPlugin.ts | 23 +- .../mention/src/getMentionOnSelectItem.ts | 13 +- packages/mention/src/types.ts | 4 +- .../slash-command/src/createSlashPlugin.ts | 23 +- packages/slash-command/src/types.ts | 2 +- .../slate/src/interfaces/element/TElement.ts | 32 +- .../slate/src/interfaces/node/TAncestor.ts | 24 +- yarn.lock | 340 ++++++----- 24 files changed, 702 insertions(+), 712 deletions(-) diff --git a/apps/www/src/lib/plate/demo/values/mentionables.ts b/apps/www/src/lib/plate/demo/values/mentionables.ts index ecc5517637..4669febd35 100644 --- a/apps/www/src/lib/plate/demo/values/mentionables.ts +++ b/apps/www/src/lib/plate/demo/values/mentionables.ts @@ -1,4 +1,4 @@ -import { TMentionItemBase } from '@udecode/plate-mention'; +import type { TMentionItemBase } from '@udecode/plate-mention'; export interface MyMentionItem extends TMentionItemBase { key: string; diff --git a/apps/www/src/registry/default/example/playground-demo.tsx b/apps/www/src/registry/default/example/playground-demo.tsx index 009d9a6f5d..5dfceeda00 100644 --- a/apps/www/src/registry/default/example/playground-demo.tsx +++ b/apps/www/src/registry/default/example/playground-demo.tsx @@ -1,26 +1,11 @@ 'use client'; import React, { useEffect, useMemo, useRef, useState } from 'react'; -import { createPlateUI } from '@/plate/create-plate-ui'; -import { CommentsProvider } from '@/plate/demo/comments/CommentsProvider'; -import { editableProps } from '@/plate/demo/editableProps'; -import { isEnabled } from '@/plate/demo/is-enabled'; -import { alignPlugin } from '@/plate/demo/plugins/alignPlugin'; -import { autoformatIndentLists } from '@/plate/demo/plugins/autoformatIndentLists'; -import { autoformatLists } from '@/plate/demo/plugins/autoformatLists'; -import { autoformatRules } from '@/plate/demo/plugins/autoformatRules'; -import { dragOverCursorPlugin } from '@/plate/demo/plugins/dragOverCursorPlugin'; -import { emojiPlugin } from '@/plate/demo/plugins/emojiPlugin'; -import { exitBreakPlugin } from '@/plate/demo/plugins/exitBreakPlugin'; -import { forcedLayoutPlugin } from '@/plate/demo/plugins/forcedLayoutPlugin'; -import { lineHeightPlugin } from '@/plate/demo/plugins/lineHeightPlugin'; -import { linkPlugin } from '@/plate/demo/plugins/linkPlugin'; -import { resetBlockTypePlugin } from '@/plate/demo/plugins/resetBlockTypePlugin'; -import { selectOnBackspacePlugin } from '@/plate/demo/plugins/selectOnBackspacePlugin'; -import { softBreakPlugin } from '@/plate/demo/plugins/softBreakPlugin'; -import { tabbablePlugin } from '@/plate/demo/plugins/tabbablePlugin'; -import { trailingBlockPlugin } from '@/plate/demo/plugins/trailingBlockPlugin'; -import { usePlaygroundValue } from '@/plate/demo/values/usePlaygroundValue'; +import { DndProvider } from 'react-dnd'; +import { HTML5Backend } from 'react-dnd-html5-backend'; + +import type { ValueId } from '@/config/customizer-plugins'; + import { cn } from '@udecode/cn'; import { createAlignPlugin } from '@udecode/plate-alignment'; import { createAutoformatPlugin } from '@udecode/plate-autoformat'; @@ -34,8 +19,8 @@ import { createUnderlinePlugin, } from '@udecode/plate-basic-marks'; import { - createBlockquotePlugin, ELEMENT_BLOCKQUOTE, + createBlockquotePlugin, } from '@udecode/plate-block-quote'; import { createExitBreakPlugin, @@ -44,16 +29,16 @@ import { } from '@udecode/plate-break'; import { createCaptionPlugin } from '@udecode/plate-caption'; import { - createCodeBlockPlugin, ELEMENT_CODE_BLOCK, + createCodeBlockPlugin, } from '@udecode/plate-code-block'; import { createComboboxPlugin } from '@udecode/plate-combobox'; import { createCommentsPlugin } from '@udecode/plate-comments'; import { - createPlugins, Plate, - PlatePluginComponent, - Value, + type PlatePluginComponent, + type Value, + createPlugins, } from '@udecode/plate-common'; import { createDndPlugin } from '@udecode/plate-dnd'; import { createEmojiPlugin } from '@udecode/plate-emoji'; @@ -64,13 +49,13 @@ import { createFontSizePlugin, } from '@udecode/plate-font'; import { - createHeadingPlugin, ELEMENT_H1, ELEMENT_H2, ELEMENT_H3, ELEMENT_H4, ELEMENT_H5, ELEMENT_H6, + createHeadingPlugin, } from '@udecode/plate-heading'; import { createHighlightPlugin } from '@udecode/plate-highlight'; import { createHorizontalRulePlugin } from '@udecode/plate-horizontal-rule'; @@ -90,8 +75,8 @@ import { createMentionPlugin } from '@udecode/plate-mention'; import { createNodeIdPlugin } from '@udecode/plate-node-id'; import { createNormalizeTypesPlugin } from '@udecode/plate-normalizers'; import { - createParagraphPlugin, ELEMENT_PARAGRAPH, + createParagraphPlugin, } from '@udecode/plate-paragraph'; import { createResetNodePlugin } from '@udecode/plate-reset-node'; import { @@ -104,16 +89,33 @@ import { createDeserializeMdPlugin } from '@udecode/plate-serializer-md'; import { createSlashPlugin } from '@udecode/plate-slash-command'; import { createTabbablePlugin } from '@udecode/plate-tabbable'; import { createTablePlugin } from '@udecode/plate-table'; -import { createTogglePlugin, ELEMENT_TOGGLE } from '@udecode/plate-toggle'; +import { ELEMENT_TOGGLE, createTogglePlugin } from '@udecode/plate-toggle'; import { createTrailingBlockPlugin } from '@udecode/plate-trailing-block'; -import { DndProvider } from 'react-dnd'; -import { HTML5Backend } from 'react-dnd-html5-backend'; -import { ValueId } from '@/config/customizer-plugins'; -import { captionPlugin } from '@/lib/plate/demo/plugins/captionPlugin'; import { settingsStore } from '@/components/context/settings-store'; import { PlaygroundFixedToolbarButtons } from '@/components/plate-ui/playground-fixed-toolbar-buttons'; import { PlaygroundFloatingToolbarButtons } from '@/components/plate-ui/playground-floating-toolbar-buttons'; +import { captionPlugin } from '@/lib/plate/demo/plugins/captionPlugin'; +import { createPlateUI } from '@/plate/create-plate-ui'; +import { CommentsProvider } from '@/plate/demo/comments/CommentsProvider'; +import { editableProps } from '@/plate/demo/editableProps'; +import { isEnabled } from '@/plate/demo/is-enabled'; +import { alignPlugin } from '@/plate/demo/plugins/alignPlugin'; +import { autoformatIndentLists } from '@/plate/demo/plugins/autoformatIndentLists'; +import { autoformatLists } from '@/plate/demo/plugins/autoformatLists'; +import { autoformatRules } from '@/plate/demo/plugins/autoformatRules'; +import { dragOverCursorPlugin } from '@/plate/demo/plugins/dragOverCursorPlugin'; +import { emojiPlugin } from '@/plate/demo/plugins/emojiPlugin'; +import { exitBreakPlugin } from '@/plate/demo/plugins/exitBreakPlugin'; +import { forcedLayoutPlugin } from '@/plate/demo/plugins/forcedLayoutPlugin'; +import { lineHeightPlugin } from '@/plate/demo/plugins/lineHeightPlugin'; +import { linkPlugin } from '@/plate/demo/plugins/linkPlugin'; +import { resetBlockTypePlugin } from '@/plate/demo/plugins/resetBlockTypePlugin'; +import { selectOnBackspacePlugin } from '@/plate/demo/plugins/selectOnBackspacePlugin'; +import { softBreakPlugin } from '@/plate/demo/plugins/softBreakPlugin'; +import { tabbablePlugin } from '@/plate/demo/plugins/tabbablePlugin'; +import { trailingBlockPlugin } from '@/plate/demo/plugins/trailingBlockPlugin'; +import { usePlaygroundValue } from '@/plate/demo/values/usePlaygroundValue'; import { CommentsPopover } from '@/registry/default/plate-ui/comments-popover'; import { CursorOverlay } from '@/registry/default/plate-ui/cursor-overlay'; import { Editor } from '@/registry/default/plate-ui/editor'; @@ -129,26 +131,26 @@ import { } from '@/registry/default/plate-ui/indent-todo-marker-component'; export const usePlaygroundPlugins = ({ - id, components = createPlateUI(), + id, }: { - id?: ValueId; components?: Record; + id?: ValueId; } = {}) => { const enabled = settingsStore.use.checkedPlugins(); const autoformatOptions = { - rules: [...autoformatRules], enableUndoOnDelete: true, + rules: [...autoformatRules], }; if (id === 'indentlist') { autoformatOptions.rules.push(...autoformatIndentLists); } else if (id === 'list') { autoformatOptions.rules.push(...autoformatLists); - } else if (!!enabled.listStyleType) { + } else if (enabled.listStyleType) { autoformatOptions.rules.push(...autoformatIndentLists); - } else if (!!enabled.list) { + } else if (enabled.list) { autoformatOptions.rules.push(...autoformatLists); } @@ -205,6 +207,7 @@ export const usePlaygroundPlugins = ({ // Block Style createAlignPlugin({ ...alignPlugin, enabled: !!enabled.align }), createIndentPlugin({ + enabled: !!enabled.indent, inject: { props: { validTypes: [ @@ -221,9 +224,9 @@ export const usePlaygroundPlugins = ({ ], }, }, - enabled: !!enabled.indent, }), createIndentListPlugin({ + enabled: id === 'indentlist' || !!enabled.listStyleType, inject: { props: { validTypes: [ @@ -240,18 +243,17 @@ export const usePlaygroundPlugins = ({ ], }, }, - enabled: id === 'indentlist' || !!enabled.listStyleType, options: { listStyleTypes: { - todo: { - type: 'todo', - markerComponent: TodoMarker, - liComponent: TodoLi, - }, fire: { - type: 'fire', - markerComponent: FireMarker, liComponent: FireLiComponent, + markerComponent: FireMarker, + type: 'fire', + }, + todo: { + liComponent: TodoLi, + markerComponent: TodoMarker, + type: 'todo', }, }, }, @@ -267,18 +269,18 @@ export const usePlaygroundPlugins = ({ options: autoformatOptions, }), createBlockSelectionPlugin({ + enabled: id === 'blockselection' || !!enabled.blockSelection, options: { sizes: { - top: 0, bottom: 0, + top: 0, }, }, - enabled: id === 'blockselection' || !!enabled.blockSelection, }), createComboboxPlugin({ enabled: !!enabled.combobox }), createDndPlugin({ - options: { enableScroller: true }, enabled: !!enabled.dnd, + options: { enableScroller: true }, }), createEmojiPlugin({ ...emojiPlugin, enabled: !!enabled.emoji }), createExitBreakPlugin({ @@ -346,12 +348,14 @@ export const useInitialValueVersion = (initialValue: Value) => { useEffect(() => { if (enabled === prevEnabled.current) return; + prevEnabled.current = enabled; setVersion((v) => v + 1); }, [enabled]); useEffect(() => { if (initialValue === prevInitialValueRef.current) return; + prevInitialValueRef.current = initialValue; setVersion((v) => v + 1); }, [initialValue]); @@ -366,24 +370,24 @@ export default function PlaygroundDemo({ id }: { id?: ValueId }) { const key = useInitialValueVersion(initialValue); const plugins = usePlaygroundPlugins({ - id, components: createPlateUI( {}, { - placeholder: isEnabled('placeholder', id), draggable: isEnabled('dnd', id), + placeholder: isEnabled('placeholder', id), } ), + id, }); return (
{enabled['fixed-toolbar'] && ( @@ -396,7 +400,6 @@ export default function PlaygroundDemo({ id }: { id?: ValueId }) {
{enabled['floating-toolbar'] && ( diff --git a/apps/www/src/registry/default/plate-ui/inline-combobox.tsx b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx index 4f3e9129e8..0cdc101928 100644 --- a/apps/www/src/registry/default/plate-ui/inline-combobox.tsx +++ b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx @@ -1,9 +1,9 @@ import React, { + type HTMLAttributes, + type ReactNode, + type RefObject, createContext, forwardRef, - HTMLAttributes, - ReactNode, - RefObject, startTransition, useContext, useEffect, @@ -12,19 +12,20 @@ import React, { useRef, useState, } from 'react'; + import { Combobox, ComboboxItem, - ComboboxItemProps, + type ComboboxItemProps, ComboboxPopover, ComboboxProvider, Portal, } from '@ariakit/react'; import { cn } from '@udecode/cn'; import { + type UseComboboxInputResult, filterWords, useComboboxInput, - UseComboboxInputResult, useHTMLInputCursorState, } from '@udecode/plate-combobox'; import { @@ -36,46 +37,46 @@ import { import { cva } from 'class-variance-authority'; type FilterFn = ( - item: { value: string; keywords?: string[] }, + item: { keywords?: string[]; value: string }, search: string ) => boolean; interface InlineComboboxContextValue { - trigger: string; - showTrigger: boolean; + dispatchVisible: (action: 'decrement' | 'increment') => void; filter: FilterFn | false; - value: string; - inputRef: RefObject; inputProps: UseComboboxInputResult['props']; + inputRef: RefObject; removeInput: UseComboboxInputResult['removeInput']; - visibleCount: number; - dispatchVisible: (action: 'increment' | 'decrement') => void; setHasEmpty: (hasEmpty: boolean) => void; + showTrigger: boolean; + trigger: string; + value: string; + visibleCount: number; } const InlineComboboxContext = createContext( null as any ); -const defaultFilter: FilterFn = ({ value, keywords = [] }, search) => +const defaultFilter: FilterFn = ({ keywords = [], value }, search) => [value, ...keywords].some((keyword) => filterWords(keyword, search)); interface InlineComboboxProps { - value?: string; - setValue?: (value: string) => void; + children: ReactNode; trigger: string; - showTrigger?: boolean; filter?: FilterFn | false; - children: ReactNode; + setValue?: (value: string) => void; + showTrigger?: boolean; + value?: string; } const InlineCombobox = ({ - value: valueProp, + children, + filter = defaultFilter, setValue: setValueProp, - trigger, showTrigger = true, - filter = defaultFilter, - children, + trigger, + value: valueProp, }: InlineComboboxProps) => { const editor = useEditorRef(); const inputRef = React.useRef(null); @@ -86,15 +87,13 @@ const InlineCombobox = ({ const value = hasValueProp ? valueProp : valueState; const setValue = hasValueProp ? setValueProp ?? (() => {}) : setValueState; - const { removeInput, props: inputProps } = useComboboxInput({ - ref: inputRef, - cursorState, + const { props: inputProps, removeInput } = useComboboxInput({ cancelInputOnBlur: false, + cursorState, onCancelInput: (cause) => { if (cause !== 'backspace') { insertText(editor, trigger + value); } - if (cause === 'arrowLeft' || cause === 'arrowRight') { moveSelection(editor, { distance: 1, @@ -102,10 +101,11 @@ const InlineCombobox = ({ }); } }, + ref: inputRef, }); const [visibleCount, dispatchVisible] = useReducer( - (state: number, action: 'increment' | 'decrement') => + (state: number, action: 'decrement' | 'increment') => state + (action === 'increment' ? 1 : -1), 0 ); @@ -114,16 +114,16 @@ const InlineCombobox = ({ const contextValue: InlineComboboxContextValue = useMemo( () => ({ - trigger, - showTrigger, + dispatchVisible, filter, - value, - inputRef, inputProps, + inputRef, removeInput, - visibleCount, - dispatchVisible, setHasEmpty, + showTrigger, + trigger, + value, + visibleCount, }), [ trigger, @@ -157,11 +157,11 @@ const InlineComboboxInput = forwardRef< HTMLAttributes >(({ className, ...props }, propRef) => { const { - inputRef: contextRef, inputProps, - value, - trigger, + inputRef: contextRef, showTrigger, + trigger, + value, } = useContext(InlineComboboxContext); const ref = useComposedRef(propRef, contextRef); @@ -186,13 +186,13 @@ const InlineComboboxInput = forwardRef< @@ -224,40 +224,40 @@ const InlineComboboxContent: typeof ComboboxPopover = ({ const comboboxItemVariants = cva( 'relative flex h-9 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none', { + defaultVariants: { + interactive: true, + }, variants: { interactive: { - true: 'cursor-pointer transition-colors hover:bg-accent hover:text-accent-foreground data-[active-item=true]:bg-accent data-[active-item=true]:text-accent-foreground', false: '', + true: 'cursor-pointer transition-colors hover:bg-accent hover:text-accent-foreground data-[active-item=true]:bg-accent data-[active-item=true]:text-accent-foreground', }, }, - defaultVariants: { - interactive: true, - }, } ); -export type InlineComboboxItemProps = ComboboxItemProps & - Required> & { - keywords?: string[]; - }; +export type InlineComboboxItemProps = { + keywords?: string[]; +} & ComboboxItemProps & + Required>; const InlineComboboxItem = ({ className, - onClick, keywords, + onClick, ...props }: InlineComboboxItemProps) => { const { value } = props; const { - value: search, - filter, dispatchVisible, + filter, removeInput, + value: search, } = useContext(InlineComboboxContext); const visible = useMemo( - () => !filter || filter({ value, keywords }, search), + () => !filter || filter({ keywords, value }, search), [filter, value, keywords, search] ); @@ -286,10 +286,10 @@ const InlineComboboxItem = ({ }; const InlineComboboxEmpty = ({ - className, children, + className, }: HTMLAttributes) => { - const { visibleCount, setHasEmpty } = useContext(InlineComboboxContext); + const { setHasEmpty, visibleCount } = useContext(InlineComboboxContext); useEffect(() => { setHasEmpty(true); @@ -312,8 +312,8 @@ const InlineComboboxEmpty = ({ export { InlineCombobox, - InlineComboboxInput, InlineComboboxContent, - InlineComboboxItem, InlineComboboxEmpty, + InlineComboboxInput, + InlineComboboxItem, }; diff --git a/apps/www/src/registry/default/plate-ui/mention-input-element.tsx b/apps/www/src/registry/default/plate-ui/mention-input-element.tsx index bedef401f6..284de6ae61 100644 --- a/apps/www/src/registry/default/plate-ui/mention-input-element.tsx +++ b/apps/www/src/registry/default/plate-ui/mention-input-element.tsx @@ -1,4 +1,5 @@ import React, { useState } from 'react'; + import { cn, withRef } from '@udecode/cn'; import { PlateElement } from '@udecode/plate-common'; import { getMentionOnSelectItem } from '@udecode/plate-mention'; @@ -17,21 +18,21 @@ const onSelectItem = getMentionOnSelectItem(); export const MentionInputElement = withRef( ({ className, ...props }, ref) => { - const { children, element, editor } = props; + const { children, editor, element } = props; const [search, setSearch] = useState(''); return ( ( {MENTIONABLES.map((item) => ( onSelectItem(editor, item, search)} + value={item.text} > {item.text} diff --git a/apps/www/src/registry/default/plate-ui/slash-input-element.tsx b/apps/www/src/registry/default/plate-ui/slash-input-element.tsx index 3bbb867230..b481daab8b 100644 --- a/apps/www/src/registry/default/plate-ui/slash-input-element.tsx +++ b/apps/www/src/registry/default/plate-ui/slash-input-element.tsx @@ -1,7 +1,8 @@ -import React, { ComponentType, SVGProps } from 'react'; +import React, { type ComponentType, type SVGProps } from 'react'; + import { withRef } from '@udecode/cn'; import { - PlateEditor, + type PlateEditor, PlateElement, toggleNodeType, } from '@udecode/plate-common'; @@ -19,36 +20,35 @@ import { } from './inline-combobox'; interface SlashCommandRule { - value: string; - keywords?: string[]; icon: ComponentType>; onSelect: (editor: PlateEditor) => void; + value: string; + keywords?: string[]; } const rules: SlashCommandRule[] = [ { - value: 'Heading 1', icon: Icons.h1, onSelect: (editor) => { toggleNodeType(editor, { activeType: ELEMENT_H1 }); }, + value: 'Heading 1', }, { - value: 'Heading 2', icon: Icons.h2, onSelect: (editor) => { toggleNodeType(editor, { activeType: ELEMENT_H2 }); }, + value: 'Heading 2', }, { - value: 'Heading 3', icon: Icons.h3, onSelect: (editor) => { toggleNodeType(editor, { activeType: ELEMENT_H3 }); }, + value: 'Heading 3', }, { - value: 'Bulleted list', icon: Icons.ul, keywords: ['ul', 'unordered list'], onSelect: (editor) => { @@ -56,9 +56,9 @@ const rules: SlashCommandRule[] = [ listStyleType: ListStyleType.Disc, }); }, + value: 'Bulleted list', }, { - value: 'Numbered list', icon: Icons.ol, keywords: ['ol', 'ordered list'], onSelect: (editor) => { @@ -66,18 +66,19 @@ const rules: SlashCommandRule[] = [ listStyleType: ListStyleType.Decimal, }); }, + value: 'Numbered list', }, ]; export const SlashInputElement = withRef( ({ className, ...props }, ref) => { - const { children, element, editor } = props; + const { children, editor, element } = props; return ( @@ -88,14 +89,14 @@ export const SlashInputElement = withRef( No matching commands found - {rules.map(({ value, keywords, icon: Icon, onSelect }) => ( + {rules.map(({ icon: Icon, keywords, onSelect, value }) => ( onSelect(editor)} + value={value} > - + {value} ))} diff --git a/apps/www/src/registry/registry.ts b/apps/www/src/registry/registry.ts index 0dfb9ec5a5..df0e3136b5 100644 --- a/apps/www/src/registry/registry.ts +++ b/apps/www/src/registry/registry.ts @@ -1,18 +1,15 @@ -import { Registry } from './schema'; +import type { Registry } from './schema'; const ui: Registry = [ { - name: 'editor', - type: 'components:plate-ui', dependencies: [], - registryDependencies: [], files: ['plate-ui/editor.tsx'], + name: 'editor', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'cloud', - type: 'components:plate-ui', dependencies: ['@udecode/plate-cloud'], - registryDependencies: [], files: [ 'plate-ui/cloud.tsx', 'plate-ui/cloud-attachment-element.tsx', @@ -21,36 +18,30 @@ const ui: Registry = [ 'plate-ui/cloud-status-bar.tsx', 'plate-ui/cloud-toolbar-buttons.tsx', ], + name: 'cloud', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'code-block-element', - type: 'components:plate-ui', dependencies: ['@udecode/plate-code-block'], - registryDependencies: ['command'], files: [ 'plate-ui/code-block-element.tsx', 'plate-ui/code-block-element.css', 'plate-ui/code-block-combobox.tsx', ], + name: 'code-block-element', + registryDependencies: ['command'], + type: 'components:plate-ui', }, { - name: 'column-element', - type: 'components:plate-ui', dependencies: ['@udecode/plate-layout'], - registryDependencies: ['command', 'resizable'], files: ['plate-ui/column-element.tsx', 'plate-ui/column-group-element.tsx'], + name: 'column-element', + registryDependencies: ['command', 'resizable'], + type: 'components:plate-ui', }, { - name: 'color-dropdown-menu', - type: 'components:plate-ui', dependencies: [], - registryDependencies: [ - 'dropdown-menu', - 'toolbar', - 'separator', - 'button', - 'tooltip', - ], files: [ 'plate-ui/color-dropdown-menu.tsx', 'plate-ui/color-constants.ts', @@ -59,12 +50,18 @@ const ui: Registry = [ 'plate-ui/color-picker.tsx', 'plate-ui/colors-custom.tsx', ], + name: 'color-dropdown-menu', + registryDependencies: [ + 'dropdown-menu', + 'toolbar', + 'separator', + 'button', + 'tooltip', + ], + type: 'components:plate-ui', }, { - name: 'comments-popover', - type: 'components:plate-ui', dependencies: ['@udecode/plate-comments'], - registryDependencies: ['popover', 'avatar'], files: [ 'plate-ui/comments-popover.tsx', 'plate-ui/comment-avatar.tsx', @@ -75,24 +72,23 @@ const ui: Registry = [ 'plate-ui/comment-resolve-button.tsx', 'plate-ui/comment-value.tsx', ], + name: 'comments-popover', + registryDependencies: ['popover', 'avatar'], + type: 'components:plate-ui', }, { - name: 'draggable', - type: 'components:plate-ui', dependencies: [ '@udecode/plate-dnd', 'react-dnd', 'react-dnd-html5-backend', ], - registryDependencies: ['tooltip'], files: ['plate-ui/draggable.tsx', 'plate-ui/with-draggables.tsx'], + name: 'draggable', + registryDependencies: ['tooltip'], + type: 'components:plate-ui', }, { - name: 'emoji-dropdown-menu', - type: 'components:plate-ui', dependencies: ['@radix-ui/react-popover'], - registryDependencies: ['toolbar'], - files: [ 'plate-ui/emoji-dropdown-menu.tsx', 'plate-ui/emoji-toolbar-dropdown.tsx', @@ -104,149 +100,153 @@ const ui: Registry = [ 'plate-ui/emoji-picker-search-and-clear.tsx', 'plate-ui/emoji-picker-search-bar.tsx', ], + name: 'emoji-dropdown-menu', + registryDependencies: ['toolbar'], + + type: 'components:plate-ui', }, { - name: 'align-dropdown-menu', - type: 'components:plate-ui', dependencies: ['@udecode/plate-alignment'], - registryDependencies: ['dropdown-menu', 'toolbar'], files: ['plate-ui/align-dropdown-menu.tsx'], + name: 'align-dropdown-menu', + registryDependencies: ['dropdown-menu', 'toolbar'], + type: 'components:plate-ui', }, { - name: 'avatar', - type: 'components:plate-ui', dependencies: ['@radix-ui/react-avatar'], - registryDependencies: [], files: ['plate-ui/avatar.tsx'], + name: 'avatar', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'blockquote-element', - type: 'components:plate-ui', dependencies: ['@udecode/plate-block-quote'], - registryDependencies: [], files: ['plate-ui/blockquote-element.tsx'], + name: 'blockquote-element', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'button', - type: 'components:plate-ui', dependencies: ['@radix-ui/react-slot'], - registryDependencies: [], files: ['plate-ui/button.tsx'], + name: 'button', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'caption', - type: 'components:plate-ui', dependencies: ['@udecode/plate-caption'], - registryDependencies: [], files: ['plate-ui/caption.tsx'], + name: 'caption', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'checkbox', - type: 'components:plate-ui', dependencies: ['@radix-ui/react-checkbox'], - registryDependencies: [], files: ['plate-ui/checkbox.tsx'], + name: 'checkbox', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'code-leaf', - type: 'components:plate-ui', dependencies: ['@udecode/plate-basic-marks'], - registryDependencies: [], files: ['plate-ui/code-leaf.tsx'], + name: 'code-leaf', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'code-line-element', - type: 'components:plate-ui', dependencies: ['@udecode/plate-code-block'], - registryDependencies: [], files: ['plate-ui/code-line-element.tsx'], + name: 'code-line-element', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'code-syntax-leaf', - type: 'components:plate-ui', dependencies: ['@udecode/plate-code-block'], - registryDependencies: [], files: ['plate-ui/code-syntax-leaf.tsx'], + name: 'code-syntax-leaf', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'combobox', - type: 'components:plate-ui', dependencies: [ '@radix-ui/react-popover', '@udecode/plate-combobox', '@udecode/plate-floating', ], - registryDependencies: [], files: ['plate-ui/combobox.tsx'], + name: 'combobox', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'command', - type: 'components:plate-ui', dependencies: ['cmdk'], - registryDependencies: ['dialog'], files: ['plate-ui/command.tsx'], + name: 'command', + registryDependencies: ['dialog'], + type: 'components:plate-ui', }, { - name: 'comment-leaf', - type: 'components:plate-ui', dependencies: ['@udecode/plate-comments'], - registryDependencies: [], files: ['plate-ui/comment-leaf.tsx'], + name: 'comment-leaf', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'comment-toolbar-button', - type: 'components:plate-ui', dependencies: [], - registryDependencies: [], files: ['plate-ui/comment-toolbar-button.tsx'], + name: 'comment-toolbar-button', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'cursor-overlay', - type: 'components:plate-ui', dependencies: [], - registryDependencies: [], files: ['plate-ui/cursor-overlay.tsx'], + name: 'cursor-overlay', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'dialog', - type: 'components:plate-ui', dependencies: ['@radix-ui/react-dialog'], - registryDependencies: [], files: ['plate-ui/dialog.tsx'], + name: 'dialog', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'dropdown-menu', - type: 'components:plate-ui', dependencies: ['@radix-ui/react-dropdown-menu'], - registryDependencies: [], files: ['plate-ui/dropdown-menu.tsx'], + name: 'dropdown-menu', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'emoji-combobox', - type: 'components:plate-ui', dependencies: ['@udecode/plate-combobox'], - registryDependencies: [], files: ['plate-ui/emoji-combobox.tsx'], + name: 'emoji-combobox', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'excalidraw-element', - type: 'components:plate-ui', dependencies: ['@udecode/plate-excalidraw'], - registryDependencies: [], files: ['plate-ui/excalidraw-element.tsx'], + name: 'excalidraw-element', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'fixed-toolbar', - type: 'components:plate-ui', dependencies: [], - registryDependencies: ['toolbar'], files: ['plate-ui/fixed-toolbar.tsx'], + name: 'fixed-toolbar', + registryDependencies: ['toolbar'], + type: 'components:plate-ui', }, { - name: 'fixed-toolbar-buttons', - type: 'components:plate-ui', dependencies: ['@udecode/plate-basic-marks'], + files: ['plate-ui/fixed-toolbar-buttons.tsx'], + name: 'fixed-toolbar-buttons', registryDependencies: [ 'toolbar', 'insert-dropdown-menu', @@ -254,476 +254,476 @@ const ui: Registry = [ 'mode-dropdown-menu', 'turn-into-dropdown-menu', ], - files: ['plate-ui/fixed-toolbar-buttons.tsx'], + type: 'components:plate-ui', }, { - name: 'floating-toolbar', - type: 'components:plate-ui', dependencies: ['@udecode/plate-floating'], - registryDependencies: ['toolbar'], files: ['plate-ui/floating-toolbar.tsx'], + name: 'floating-toolbar', + registryDependencies: ['toolbar'], + type: 'components:plate-ui', }, { - name: 'floating-toolbar-buttons', - type: 'components:plate-ui', dependencies: ['@udecode/plate-basic-marks'], + files: ['plate-ui/floating-toolbar-buttons.tsx'], + name: 'floating-toolbar-buttons', registryDependencies: [ 'mark-toolbar-button', 'more-dropdown-menu', 'turn-into-dropdown-menu', ], - files: ['plate-ui/floating-toolbar-buttons.tsx'], + type: 'components:plate-ui', }, { - name: 'heading-element', - type: 'components:plate-ui', dependencies: ['@udecode/plate-heading'], - registryDependencies: [], files: ['plate-ui/heading-element.tsx'], + name: 'heading-element', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'highlight-leaf', - type: 'components:plate-ui', dependencies: ['@udecode/plate-highlight'], - registryDependencies: [], files: ['plate-ui/highlight-leaf.tsx'], + name: 'highlight-leaf', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'hr-element', - type: 'components:plate-ui', dependencies: ['@udecode/plate-horizontal-rule'], - registryDependencies: [], files: ['plate-ui/hr-element.tsx'], + name: 'hr-element', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'image-element', - type: 'components:plate-ui', dependencies: ['@udecode/plate-media'], - registryDependencies: ['media-popover', 'caption', 'resizable'], files: ['plate-ui/image-element.tsx'], + name: 'image-element', + registryDependencies: ['media-popover', 'caption', 'resizable'], + type: 'components:plate-ui', }, { - name: 'indent-list-toolbar-button', - type: 'components:plate-ui', dependencies: ['@udecode/plate-indent-list'], - registryDependencies: ['toolbar'], files: ['plate-ui/indent-list-toolbar-button.tsx'], + name: 'indent-list-toolbar-button', + registryDependencies: ['toolbar'], + type: 'components:plate-ui', }, { - name: 'indent-toolbar-button', - type: 'components:plate-ui', dependencies: ['@udecode/plate-indent'], - registryDependencies: ['toolbar'], files: ['plate-ui/indent-toolbar-button.tsx'], + name: 'indent-toolbar-button', + registryDependencies: ['toolbar'], + type: 'components:plate-ui', }, { - name: 'input', - type: 'components:plate-ui', dependencies: [], - registryDependencies: [], files: ['plate-ui/input.tsx'], + name: 'input', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'insert-dropdown-menu', - type: 'components:plate-ui', dependencies: [ '@udecode/plate-block-quote', '@udecode/plate-heading', '@udecode/plate-paragraph', ], - registryDependencies: ['dropdown-menu', 'toolbar'], files: ['plate-ui/insert-dropdown-menu.tsx'], + name: 'insert-dropdown-menu', + registryDependencies: ['dropdown-menu', 'toolbar'], + type: 'components:plate-ui', }, { - name: 'kbd-leaf', - type: 'components:plate-ui', dependencies: ['@udecode/plate-kbd'], - registryDependencies: [], files: ['plate-ui/kbd-leaf.tsx'], + name: 'kbd-leaf', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'line-height-dropdown-menu', - type: 'components:plate-ui', dependencies: ['@udecode/plate-line-height'], - registryDependencies: ['toolbar', 'dropdown-menu'], files: ['plate-ui/line-height-dropdown-menu.tsx'], + name: 'line-height-dropdown-menu', + registryDependencies: ['toolbar', 'dropdown-menu'], + type: 'components:plate-ui', }, { - name: 'link-element', - type: 'components:plate-ui', dependencies: ['@udecode/plate-link'], - registryDependencies: [], files: ['plate-ui/link-element.tsx'], + name: 'link-element', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'link-floating-toolbar', - type: 'components:plate-ui', dependencies: ['@udecode/plate-link'], - registryDependencies: ['button', 'input', 'popover', 'separator'], files: ['plate-ui/link-floating-toolbar.tsx'], + name: 'link-floating-toolbar', + registryDependencies: ['button', 'input', 'popover', 'separator'], + type: 'components:plate-ui', }, { - name: 'link-toolbar-button', - type: 'components:plate-ui', dependencies: ['@udecode/plate-link'], - registryDependencies: ['toolbar'], files: ['plate-ui/link-toolbar-button.tsx'], + name: 'link-toolbar-button', + registryDependencies: ['toolbar'], + type: 'components:plate-ui', }, { - name: 'list-element', - type: 'components:plate-ui', dependencies: ['@udecode/plate-list'], - registryDependencies: [], files: ['plate-ui/list-element.tsx'], + name: 'list-element', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'list-toolbar-button', - type: 'components:plate-ui', dependencies: ['@udecode/plate-list'], - registryDependencies: ['toolbar'], files: ['plate-ui/list-toolbar-button.tsx'], + name: 'list-toolbar-button', + registryDependencies: ['toolbar'], + type: 'components:plate-ui', }, { - name: 'mark-toolbar-button', - type: 'components:plate-ui', dependencies: ['@udecode/plate-basic-marks'], - registryDependencies: ['toolbar'], files: ['plate-ui/mark-toolbar-button.tsx'], + name: 'mark-toolbar-button', + registryDependencies: ['toolbar'], + type: 'components:plate-ui', }, { - name: 'media-embed-element', - type: 'components:plate-ui', dependencies: [ '@udecode/plate-media', 'react-tweet', 'react-lite-youtube-embed', ], - registryDependencies: ['media-popover', 'caption', 'resizable'], files: ['plate-ui/media-embed-element.tsx'], + name: 'media-embed-element', + registryDependencies: ['media-popover', 'caption', 'resizable'], + type: 'components:plate-ui', }, { - name: 'media-popover', - type: 'components:plate-ui', dependencies: ['@udecode/plate-media'], - registryDependencies: ['button', 'input', 'popover', 'separator'], files: ['plate-ui/media-popover.tsx'], + name: 'media-popover', + registryDependencies: ['button', 'input', 'popover', 'separator'], + type: 'components:plate-ui', }, { - name: 'media-toolbar-button', - type: 'components:plate-ui', dependencies: ['@udecode/plate-media'], - registryDependencies: ['toolbar'], files: ['plate-ui/media-toolbar-button.tsx'], + name: 'media-toolbar-button', + registryDependencies: ['toolbar'], + type: 'components:plate-ui', }, { - name: 'mention-element', - type: 'components:plate-ui', dependencies: ['@udecode/plate-mention'], - registryDependencies: [], files: ['plate-ui/mention-element.tsx'], + name: 'mention-element', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'mention-input-element', - type: 'components:plate-ui', dependencies: ['@udecode/plate-mention'], - registryDependencies: [], files: ['plate-ui/mention-input-element.tsx'], + name: 'mention-input-element', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'mode-dropdown-menu', - type: 'components:plate-ui', dependencies: [], - registryDependencies: ['dropdown-menu', 'toolbar'], files: ['plate-ui/mode-dropdown-menu.tsx'], + name: 'mode-dropdown-menu', + registryDependencies: ['dropdown-menu', 'toolbar'], + type: 'components:plate-ui', }, { - name: 'more-dropdown-menu', - type: 'components:plate-ui', dependencies: ['@udecode/plate-basic-marks'], - registryDependencies: ['dropdown-menu', 'toolbar'], files: ['plate-ui/more-dropdown-menu.tsx'], + name: 'more-dropdown-menu', + registryDependencies: ['dropdown-menu', 'toolbar'], + type: 'components:plate-ui', }, { - name: 'outdent-toolbar-button', - type: 'components:plate-ui', dependencies: ['@udecode/plate-indent'], - registryDependencies: ['toolbar'], files: ['plate-ui/outdent-toolbar-button.tsx'], + name: 'outdent-toolbar-button', + registryDependencies: ['toolbar'], + type: 'components:plate-ui', }, { - name: 'paragraph-element', - type: 'components:plate-ui', dependencies: ['@udecode/plate-paragraph'], - registryDependencies: [], files: ['plate-ui/paragraph-element.tsx'], + name: 'paragraph-element', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'placeholder', - type: 'components:plate-ui', dependencies: ['@udecode/plate-heading'], - registryDependencies: ['paragraph-element'], files: ['plate-ui/placeholder.tsx'], + name: 'placeholder', + registryDependencies: ['paragraph-element'], + type: 'components:plate-ui', }, { - name: 'popover', - type: 'components:plate-ui', dependencies: ['@radix-ui/react-popover'], - registryDependencies: [], files: ['plate-ui/popover.tsx'], + name: 'popover', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'search-highlight-leaf', - type: 'components:plate-ui', dependencies: ['@udecode/plate-find-replace'], - registryDependencies: [], files: ['plate-ui/search-highlight-leaf.tsx'], + name: 'search-highlight-leaf', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'separator', - type: 'components:plate-ui', dependencies: ['@radix-ui/react-separator'], - registryDependencies: [], files: ['plate-ui/separator.tsx'], + name: 'separator', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'table-cell-element', - type: 'components:plate-ui', dependencies: ['@udecode/plate-table'], - registryDependencies: ['resizable'], files: ['plate-ui/table-cell-element.tsx'], + name: 'table-cell-element', + registryDependencies: ['resizable'], + type: 'components:plate-ui', }, { - name: 'table-dropdown-menu', - type: 'components:plate-ui', dependencies: ['@udecode/plate-table'], - registryDependencies: ['dropdown-menu', 'toolbar'], files: ['plate-ui/table-dropdown-menu.tsx'], + name: 'table-dropdown-menu', + registryDependencies: ['dropdown-menu', 'toolbar'], + type: 'components:plate-ui', }, { - name: 'table-element', - type: 'components:plate-ui', dependencies: ['@udecode/plate-table'], - registryDependencies: ['dropdown-menu'], files: ['plate-ui/table-element.tsx'], + name: 'table-element', + registryDependencies: ['dropdown-menu'], + type: 'components:plate-ui', }, { - name: 'table-row-element', - type: 'components:plate-ui', dependencies: ['@udecode/plate-table'], - registryDependencies: [], files: ['plate-ui/table-row-element.tsx'], + name: 'table-row-element', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'todo-list-element', - type: 'components:plate-ui', dependencies: ['@udecode/plate-list'], - registryDependencies: ['checkbox'], files: ['plate-ui/todo-list-element.tsx'], + name: 'todo-list-element', + registryDependencies: ['checkbox'], + type: 'components:plate-ui', }, { - name: 'toggle-element', - type: 'components:plate-ui', dependencies: ['@udecode/plate-toggle'], - registryDependencies: [], files: ['plate-ui/toggle-element.tsx'], + name: 'toggle-element', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'toggle-toolbar-button', - type: 'components:plate-ui', dependencies: ['@udecode/plate-toggle'], - registryDependencies: ['toolbar'], files: ['plate-ui/toggle-toolbar-button.tsx'], + name: 'toggle-toolbar-button', + registryDependencies: ['toolbar'], + type: 'components:plate-ui', }, { - name: 'toolbar', - type: 'components:plate-ui', dependencies: ['@radix-ui/react-toolbar'], - registryDependencies: ['tooltip', 'separator'], files: ['plate-ui/toolbar.tsx'], + name: 'toolbar', + registryDependencies: ['tooltip', 'separator'], + type: 'components:plate-ui', }, { - name: 'tooltip', - type: 'components:plate-ui', dependencies: ['@radix-ui/react-tooltip'], - registryDependencies: [], files: ['plate-ui/tooltip.tsx'], + name: 'tooltip', + registryDependencies: [], + type: 'components:plate-ui', }, { - name: 'turn-into-dropdown-menu', - type: 'components:plate-ui', dependencies: [ '@udecode/plate-block-quote', '@udecode/plate-heading', '@udecode/plate-paragraph', ], - registryDependencies: ['dropdown-menu', 'toolbar'], files: ['plate-ui/turn-into-dropdown-menu.tsx'], + name: 'turn-into-dropdown-menu', + registryDependencies: ['dropdown-menu', 'toolbar'], + type: 'components:plate-ui', }, { - name: 'resizable', - type: 'components:plate-ui', dependencies: ['@udecode/plate-resizable'], - registryDependencies: [], files: ['plate-ui/resizable.tsx'], + name: 'resizable', + registryDependencies: [], + type: 'components:plate-ui', }, ]; const example: Registry = [ { + files: ['example/editor-default.tsx'], name: 'editor-default', - type: 'components:example', registryDependencies: [], - files: ['example/editor-default.tsx'], + type: 'components:example', }, { + files: ['example/editor-disabled.tsx'], name: 'editor-disabled', - type: 'components:example', registryDependencies: [], - files: ['example/editor-disabled.tsx'], + type: 'components:example', }, { + files: ['example/editor-ghost.tsx'], name: 'editor-ghost', - type: 'components:example', registryDependencies: [], - files: ['example/editor-ghost.tsx'], + type: 'components:example', }, { + files: ['example/editor-label.tsx'], name: 'editor-label', - type: 'components:example', registryDependencies: [], - files: ['example/editor-label.tsx'], + type: 'components:example', }, { + files: ['example/editor-text.tsx'], name: 'editor-text', - type: 'components:example', registryDependencies: [], - files: ['example/editor-text.tsx'], + type: 'components:example', }, { + files: ['example/editor-button.tsx'], name: 'editor-button', - type: 'components:example', registryDependencies: [], - files: ['example/editor-button.tsx'], + type: 'components:example', }, { + files: ['example/editor-form.tsx'], name: 'editor-form', - type: 'components:example', registryDependencies: [], - files: ['example/editor-form.tsx'], + type: 'components:example', }, { + files: ['example/basic-editor-default-demo.tsx'], name: 'basic-editor-default-demo', - type: 'components:example', registryDependencies: [], - files: ['example/basic-editor-default-demo.tsx'], + type: 'components:example', }, { + files: ['example/basic-editor-styling-demo.tsx'], name: 'basic-editor-styling-demo', - type: 'components:example', registryDependencies: [], - files: ['example/basic-editor-styling-demo.tsx'], + type: 'components:example', }, { + files: ['example/basic-editor-handler-demo.tsx'], name: 'basic-editor-handler-demo', - type: 'components:example', registryDependencies: [], - files: ['example/basic-editor-handler-demo.tsx'], + type: 'components:example', }, { + files: ['example/basic-editor-value-demo.tsx'], name: 'basic-editor-value-demo', - type: 'components:example', registryDependencies: [], - files: ['example/basic-editor-value-demo.tsx'], + type: 'components:example', }, { + files: ['example/basic-plugins-components-demo.tsx'], name: 'basic-plugins-components-demo', - type: 'components:example', registryDependencies: [], - files: ['example/basic-plugins-components-demo.tsx'], + type: 'components:example', }, { + files: ['example/basic-plugins-default-demo.tsx'], name: 'basic-plugins-default-demo', - type: 'components:example', registryDependencies: [], - files: ['example/basic-plugins-default-demo.tsx'], + type: 'components:example', }, { + files: ['example/cloud-demo.tsx'], name: 'cloud-demo', - type: 'components:example', registryDependencies: [], - files: ['example/cloud-demo.tsx'], + type: 'components:example', }, { + files: ['example/editable-voids-demo.tsx'], name: 'editable-voids-demo', - type: 'components:example', registryDependencies: [], - files: ['example/editable-voids-demo.tsx'], + type: 'components:example', }, { + files: ['example/find-replace-demo.tsx'], name: 'find-replace-demo', - type: 'components:example', registryDependencies: [], - files: ['example/find-replace-demo.tsx'], + type: 'components:example', }, { + files: ['example/hundreds-blocks-demo.tsx'], name: 'hundreds-blocks-demo', - type: 'components:example', registryDependencies: [], - files: ['example/hundreds-blocks-demo.tsx'], + type: 'components:example', }, { + files: ['example/hundreds-editors-demo.tsx'], name: 'hundreds-editors-demo', - type: 'components:example', registryDependencies: [], - files: ['example/hundreds-editors-demo.tsx'], + type: 'components:example', }, { + files: ['example/iframe-demo.tsx'], name: 'iframe-demo', - type: 'components:example', registryDependencies: [], - files: ['example/iframe-demo.tsx'], + type: 'components:example', }, { + files: ['example/mode-toggle.tsx'], name: 'mode-toggle', type: 'components:example', - files: ['example/mode-toggle.tsx'], }, { + files: ['example/multiple-editors-demo.tsx'], name: 'multiple-editors-demo', - type: 'components:example', registryDependencies: [], - files: ['example/multiple-editors-demo.tsx'], + type: 'components:example', }, { + files: ['example/version-history-demo.tsx'], name: 'version-history-demo', - type: 'components:example', registryDependencies: [], - files: ['example/version-history-demo.tsx'], + type: 'components:example', }, { + files: ['example/playground-demo.tsx'], name: 'playground-demo', - type: 'components:example', registryDependencies: [], - files: ['example/playground-demo.tsx'], + type: 'components:example', }, { + files: ['example/preview-md-demo.tsx'], name: 'preview-md-demo', - type: 'components:example', registryDependencies: [], - files: ['example/preview-md-demo.tsx'], + type: 'components:example', }, { - name: 'globals', - type: 'components:component', external: true, files: ['styles/globals.css'], + name: 'globals', + type: 'components:component', }, { - name: 'plate-types', - type: 'components:component', external: true, files: ['types/plate-types.ts'], + name: 'plate-types', + type: 'components:component', }, ]; diff --git a/packages/combobox/src/hooks/useComboboxInput.ts b/packages/combobox/src/hooks/useComboboxInput.ts index 8db8843e1b..f135e54119 100644 --- a/packages/combobox/src/hooks/useComboboxInput.ts +++ b/packages/combobox/src/hooks/useComboboxInput.ts @@ -1,14 +1,15 @@ import { - HTMLAttributes, - RefObject, + type HTMLAttributes, + type RefObject, useCallback, useEffect, useRef, } from 'react'; + import { + Hotkeys, findNodePath, focusEditor, - Hotkeys, isHotkey, removeNodes, useEditorRef, @@ -16,41 +17,44 @@ import { } from '@udecode/plate-common'; import { useSelected } from 'slate-react'; -import { CancelComboboxInputCause, ComboboxInputCursorState } from '../types'; +import type { + CancelComboboxInputCause, + ComboboxInputCursorState, +} from '../types'; export interface UseComboboxInputOptions { ref: RefObject; - cursorState?: ComboboxInputCursorState; autoFocus?: boolean; - cancelInputOnEscape?: boolean; - cancelInputOnBackspace?: boolean; cancelInputOnArrowLeftRight?: boolean; - cancelInputOnDeselect?: boolean; + cancelInputOnBackspace?: boolean; cancelInputOnBlur?: boolean; + cancelInputOnDeselect?: boolean; + cancelInputOnEscape?: boolean; + cursorState?: ComboboxInputCursorState; forwardUndoRedoToEditor?: boolean; onCancelInput?: (cause: CancelComboboxInputCause) => void; } export interface UseComboboxInputResult { - removeInput: (focusEditor?: boolean) => void; cancelInput: ( cause?: CancelComboboxInputCause, focusEditor?: boolean ) => void; - props: Required, 'onKeyDown' | 'onBlur'>>; + props: Required, 'onBlur' | 'onKeyDown'>>; + removeInput: (focusEditor?: boolean) => void; } export const useComboboxInput = ({ - ref, - cursorState, autoFocus = true, - cancelInputOnEscape = true, - cancelInputOnBackspace = true, cancelInputOnArrowLeftRight = true, - cancelInputOnDeselect = true, + cancelInputOnBackspace = true, cancelInputOnBlur = true, + cancelInputOnDeselect = true, + cancelInputOnEscape = true, + cursorState, forwardUndoRedoToEditor = true, onCancelInput, + ref, }: UseComboboxInputOptions): UseComboboxInputResult => { const editor = useEditorRef(); const element = useElement(); @@ -62,7 +66,9 @@ export const useComboboxInput = ({ const removeInput = useCallback( (shouldFocusEditor = false) => { const path = findNodePath(editor, element); + if (!path) return; + removeNodes(editor, { at: path }); if (shouldFocusEditor) { @@ -81,8 +87,8 @@ export const useComboboxInput = ({ ); /** - * Using autoFocus on the input element causes an error: - * Cannot resolve a Slate node from DOM node: [object HTMLSpanElement] + * Using autoFocus on the input element causes an error: Cannot resolve a + * Slate node from DOM node: [object HTMLSpanElement] */ useEffect(() => { if (autoFocus) { @@ -91,10 +97,10 @@ export const useComboboxInput = ({ }, [autoFocus, ref]); /** - * Storing the previous selection lets us determine whether the input has - * been actively deselected. When undoing or redoing causes a combobox input - * to be inserted, selected can be temporarily false. Removing the input at - * this point is incorrect and crashes the editor. + * Storing the previous selection lets us determine whether the input has been + * actively deselected. When undoing or redoing causes a combobox input to be + * inserted, selected can be temporarily false. Removing the input at this + * point is incorrect and crashes the editor. */ const previousSelected = useRef(selected); @@ -107,14 +113,17 @@ export const useComboboxInput = ({ }, [selected, cancelInputOnDeselect, cancelInput]); return { - removeInput, cancelInput, props: { + onBlur: () => { + if (cancelInputOnBlur) { + cancelInput('blur'); + } + }, onKeyDown: (event) => { if (cancelInputOnEscape && isHotkey('escape', event)) { cancelInput('escape', true); } - if ( cancelInputOnBackspace && cursorAtStart && @@ -122,7 +131,6 @@ export const useComboboxInput = ({ ) { cancelInput('backspace', true); } - if ( cancelInputOnArrowLeftRight && cursorAtStart && @@ -130,7 +138,6 @@ export const useComboboxInput = ({ ) { cancelInput('arrowLeft', true); } - if ( cancelInputOnArrowLeftRight && cursorAtEnd && @@ -148,11 +155,7 @@ export const useComboboxInput = ({ focusEditor(editor); } }, - onBlur: () => { - if (cancelInputOnBlur) { - cancelInput('blur'); - } - }, }, + removeInput, }; }; diff --git a/packages/combobox/src/hooks/useHTMLInputCursorState.ts b/packages/combobox/src/hooks/useHTMLInputCursorState.ts index 3655347c6d..24d6cfd473 100644 --- a/packages/combobox/src/hooks/useHTMLInputCursorState.ts +++ b/packages/combobox/src/hooks/useHTMLInputCursorState.ts @@ -1,23 +1,23 @@ -import { RefObject, useCallback, useEffect, useState } from 'react'; +import { type RefObject, useCallback, useEffect, useState } from 'react'; -import { ComboboxInputCursorState } from '../types'; +import type { ComboboxInputCursorState } from '../types'; export const useHTMLInputCursorState = ( ref: RefObject ): ComboboxInputCursorState => { const [cursorState, setCursorState] = useState({ - atStart: false, atEnd: false, + atStart: false, }); const recomputeCursorState = useCallback(() => { if (!ref.current) return; - const { selectionStart, selectionEnd, value } = ref.current; + const { selectionEnd, selectionStart, value } = ref.current; setCursorState({ - atStart: selectionStart === 0, atEnd: selectionEnd === value.length, + atStart: selectionStart === 0, }); }, [ref]); @@ -25,6 +25,7 @@ export const useHTMLInputCursorState = ( recomputeCursorState(); const input = ref.current; + if (!input) return; input.addEventListener('input', recomputeCursorState); diff --git a/packages/combobox/src/legacy-combobox-delete-me/hooks/index.ts b/packages/combobox/src/legacy-combobox-delete-me/hooks/index.ts index 2426758fb2..21604fd34b 100644 --- a/packages/combobox/src/legacy-combobox-delete-me/hooks/index.ts +++ b/packages/combobox/src/legacy-combobox-delete-me/hooks/index.ts @@ -1,6 +1,4 @@ -/** - * @file Automatically generated by barrelsby. - */ +/** @file Automatically generated by barrelsby. */ export * from './useComboboxContent'; export * from './useComboboxControls'; diff --git a/packages/combobox/src/legacy-combobox-delete-me/index.ts b/packages/combobox/src/legacy-combobox-delete-me/index.ts index a198a8b6e5..020be02fe9 100644 --- a/packages/combobox/src/legacy-combobox-delete-me/index.ts +++ b/packages/combobox/src/legacy-combobox-delete-me/index.ts @@ -1,6 +1,4 @@ -/** - * @file Automatically generated by barrelsby. - */ +/** @file Automatically generated by barrelsby. */ export * from './combobox.store'; export * from './createComboboxPlugin'; diff --git a/packages/combobox/src/legacy-combobox-delete-me/utils/index.ts b/packages/combobox/src/legacy-combobox-delete-me/utils/index.ts index 0390c0ab42..1864c7ebf3 100644 --- a/packages/combobox/src/legacy-combobox-delete-me/utils/index.ts +++ b/packages/combobox/src/legacy-combobox-delete-me/utils/index.ts @@ -1,6 +1,4 @@ -/** - * @file Automatically generated by barrelsby. - */ +/** @file Automatically generated by barrelsby. */ export * from './getNextNonDisabledIndex'; export * from './getNextWrappingIndex'; diff --git a/packages/combobox/src/types.ts b/packages/combobox/src/types.ts index 7929bcc48d..5683f4ee42 100644 --- a/packages/combobox/src/types.ts +++ b/packages/combobox/src/types.ts @@ -1,22 +1,22 @@ -import { PlateEditor, TElement } from '@udecode/plate-common'; +import type { PlateEditor, TElement } from '@udecode/plate-common'; export interface TriggerComboboxPlugin { - trigger?: string | string[] | RegExp; + createComboboxInput?: (trigger: string) => TElement; + trigger?: RegExp | string | string[]; triggerPreviousCharPattern?: RegExp; triggerQuery?: (editor: PlateEditor) => boolean; - createComboboxInput?: (trigger: string) => TElement; } export type ComboboxInputCursorState = { - atStart: boolean; atEnd: boolean; + atStart: boolean; }; export type CancelComboboxInputCause = - | 'manual' - | 'escape' - | 'backspace' | 'arrowLeft' | 'arrowRight' + | 'backspace' + | 'blur' | 'deselect' - | 'blur'; + | 'escape' + | 'manual'; diff --git a/packages/combobox/src/utils/filterWords.spec.ts b/packages/combobox/src/utils/filterWords.spec.ts index 2c939fa40d..09ad706710 100644 --- a/packages/combobox/src/utils/filterWords.spec.ts +++ b/packages/combobox/src/utils/filterWords.spec.ts @@ -1,4 +1,4 @@ -import { filterWords, FilterWordsOptions } from './filterWords'; +import { type FilterWordsOptions, filterWords } from './filterWords'; describe('filterWords', () => { describe('with default options', () => { diff --git a/packages/combobox/src/utils/filterWords.ts b/packages/combobox/src/utils/filterWords.ts index 4816179f92..de913d4549 100644 --- a/packages/combobox/src/utils/filterWords.ts +++ b/packages/combobox/src/utils/filterWords.ts @@ -1,5 +1,5 @@ export interface FilterWordsOptions { - prefixMode?: 'none' | 'all-words' | 'last-word'; + prefixMode?: 'all-words' | 'last-word' | 'none'; wordQuantifier?: 'match-all' | 'match-any'; } @@ -38,8 +38,8 @@ export const filterWords = ( return ( haystackWord.localeCompare(needleWord, undefined, { - usage: 'search', sensitivity: 'base', + usage: 'search', }) === 0 ); }); diff --git a/packages/combobox/src/withTriggerCombobox.spec.tsx b/packages/combobox/src/withTriggerCombobox.spec.tsx index fc3c6dfe58..f766acb592 100644 --- a/packages/combobox/src/withTriggerCombobox.spec.tsx +++ b/packages/combobox/src/withTriggerCombobox.spec.tsx @@ -1,7 +1,7 @@ /** @jsx jsx */ import { - TriggerComboboxPlugin, + type TriggerComboboxPlugin, withTriggerCombobox, } from '@udecode/plate-combobox'; import { createPlateEditor, createPluginFactory } from '@udecode/plate-common'; @@ -10,15 +10,15 @@ import { jsx } from '@udecode/plate-test-utils'; const createExampleComboboxPlugin = createPluginFactory({ key: 'exampleCombobox', - withOverrides: withTriggerCombobox, plugins: [ { - key: 'mention_input', isElement: true, isInline: true, isVoid: true, + key: 'mention_input', }, ], + withOverrides: withTriggerCombobox, }); const plugins = [ @@ -27,26 +27,26 @@ const plugins = [ createExampleComboboxPlugin({ key: 'exampleCombobox1', options: { - trigger: ['@', '#'], - triggerPreviousCharPattern: /^$|^[\s"']$/, createComboboxInput: (trigger) => ({ - type: 'mention_input', - trigger, children: [{ text: '' }], + trigger, + type: 'mention_input', }), + trigger: ['@', '#'], + triggerPreviousCharPattern: /^$|^[\s"']$/, }, }), createExampleComboboxPlugin({ key: 'exampleCombobox2', options: { - trigger: ':', - triggerPreviousCharPattern: /^\s?$/, createComboboxInput: () => ({ - type: 'mention_input', - trigger: ':', children: [{ text: '' }], + trigger: ':', + type: 'mention_input', }), + trigger: ':', + triggerPreviousCharPattern: /^\s?$/, }, }), ]; diff --git a/packages/combobox/src/withTriggerCombobox.ts b/packages/combobox/src/withTriggerCombobox.ts index 7916f6c5fb..47f905e10b 100644 --- a/packages/combobox/src/withTriggerCombobox.ts +++ b/packages/combobox/src/withTriggerCombobox.ts @@ -1,14 +1,14 @@ import { + type PlateEditor, + type TElement, + type Value, + type WithPlatePlugin, getEditorString, getPointBefore, getRange, - PlateEditor, - TElement, - Value, - WithPlatePlugin, } from '@udecode/plate-common'; -import { TriggerComboboxPlugin } from './types'; +import type { TriggerComboboxPlugin } from './types'; export const withTriggerCombobox = < V extends Value = Value, @@ -16,13 +16,13 @@ export const withTriggerCombobox = < >( editor: E, { - type, options: { + createComboboxInput, trigger, triggerPreviousCharPattern, triggerQuery, - createComboboxInput, }, + type, }: WithPlatePlugin ) => { const { insertText } = editor; @@ -31,7 +31,6 @@ export const withTriggerCombobox = < if (trigger instanceof RegExp) { return trigger.test(text); } - if (Array.isArray(trigger)) { return trigger.includes(text); } @@ -64,7 +63,7 @@ export const withTriggerCombobox = < if (matchesPreviousCharPattern) { const inputNode: TElement = createComboboxInput ? createComboboxInput(text) - : { type, children: [{ text: '' }] }; + : { children: [{ text: '' }], type }; return editor.insertNode(inputNode); } diff --git a/packages/mention/src/createMentionPlugin.ts b/packages/mention/src/createMentionPlugin.ts index d0359c23ec..a3707bbc20 100644 --- a/packages/mention/src/createMentionPlugin.ts +++ b/packages/mention/src/createMentionPlugin.ts @@ -1,37 +1,36 @@ import { withTriggerCombobox } from '@udecode/plate-combobox'; import { createPluginFactory } from '@udecode/plate-common'; -import { MentionPlugin } from './types'; +import type { MentionPlugin } from './types'; export const ELEMENT_MENTION = 'mention'; + export const ELEMENT_MENTION_INPUT = 'mention_input'; -/** - * Enables support for autocompleting @mentions. - */ +/** Enables support for autocompleting @mentions. */ export const createMentionPlugin = createPluginFactory({ - key: ELEMENT_MENTION, isElement: true, isInline: true, - isVoid: true, isMarkableVoid: true, - withOverrides: withTriggerCombobox, + isVoid: true, + key: ELEMENT_MENTION, options: { - trigger: '@', - triggerPreviousCharPattern: /^\s?$/, createComboboxInput: (trigger) => ({ - type: ELEMENT_MENTION_INPUT, - trigger, children: [{ text: '' }], + trigger, + type: ELEMENT_MENTION_INPUT, }), createMentionNode: (item) => ({ value: item.text }), + trigger: '@', + triggerPreviousCharPattern: /^\s?$/, }, plugins: [ { - key: ELEMENT_MENTION_INPUT, isElement: true, isInline: true, isVoid: true, + key: ELEMENT_MENTION_INPUT, }, ], + withOverrides: withTriggerCombobox, }); diff --git a/packages/mention/src/getMentionOnSelectItem.ts b/packages/mention/src/getMentionOnSelectItem.ts index 1e1a3f4750..655808a877 100644 --- a/packages/mention/src/getMentionOnSelectItem.ts +++ b/packages/mention/src/getMentionOnSelectItem.ts @@ -1,17 +1,18 @@ import { + type PlateEditor, + type PlatePluginKey, + type Value, getBlockAbove, getPlugin, insertNodes, insertText, isEndPoint, moveSelection, - PlateEditor, - PlatePluginKey, - Value, } from '@udecode/plate-common'; +import type { MentionPlugin, TMentionElement, TMentionItemBase } from './types'; + import { ELEMENT_MENTION } from './createMentionPlugin'; -import { MentionPlugin, TMentionElement, TMentionItemBase } from './types'; export type MentionOnSelectItem< TItem extends TMentionItemBase = TMentionItemBase, @@ -27,15 +28,15 @@ export const getMentionOnSelectItem = }: PlatePluginKey = {}): MentionOnSelectItem => (editor, item, search = '') => { const { + options: { createMentionNode, insertSpaceAfterMention }, type, - options: { insertSpaceAfterMention, createMentionNode }, } = getPlugin(editor as any, key); const props = createMentionNode!(item, search); insertNodes(editor, { - type, children: [{ text: '' }], + type, ...props, } as TMentionElement); diff --git a/packages/mention/src/types.ts b/packages/mention/src/types.ts index 07e596e8e1..3cfff08020 100644 --- a/packages/mention/src/types.ts +++ b/packages/mention/src/types.ts @@ -1,5 +1,5 @@ -import { TriggerComboboxPlugin } from '@udecode/plate-combobox'; -import { TElement, TNodeProps } from '@udecode/plate-common'; +import type { TriggerComboboxPlugin } from '@udecode/plate-combobox'; +import type { TElement, TNodeProps } from '@udecode/plate-common'; export interface TMentionItemBase { text: string; diff --git a/packages/slash-command/src/createSlashPlugin.ts b/packages/slash-command/src/createSlashPlugin.ts index 2864c4da4a..e9afd640d0 100644 --- a/packages/slash-command/src/createSlashPlugin.ts +++ b/packages/slash-command/src/createSlashPlugin.ts @@ -1,29 +1,30 @@ import { - TriggerComboboxPlugin, + type TriggerComboboxPlugin, withTriggerCombobox, } from '@udecode/plate-combobox'; import { createPluginFactory } from '@udecode/plate-common'; export const KEY_SLASH_COMMAND = 'slash_command'; + export const ELEMENT_SLASH_INPUT = 'slash_input'; export const createSlashPlugin = createPluginFactory({ key: KEY_SLASH_COMMAND, - withOverrides: withTriggerCombobox, + options: { + createComboboxInput: () => ({ + children: [{ text: '' }], + type: ELEMENT_SLASH_INPUT, + }), + trigger: '/', + triggerPreviousCharPattern: /^\s?$/, + }, plugins: [ { - key: ELEMENT_SLASH_INPUT, isElement: true, isInline: true, isVoid: true, + key: ELEMENT_SLASH_INPUT, }, ], - options: { - trigger: '/', - triggerPreviousCharPattern: /^\s?$/, - createComboboxInput: () => ({ - type: ELEMENT_SLASH_INPUT, - children: [{ text: '' }], - }), - }, + withOverrides: withTriggerCombobox, }); diff --git a/packages/slash-command/src/types.ts b/packages/slash-command/src/types.ts index 8af9663287..3c3e8de5ec 100644 --- a/packages/slash-command/src/types.ts +++ b/packages/slash-command/src/types.ts @@ -1,3 +1,3 @@ -import { TElement } from '@udecode/plate-common'; +import type { TElement } from '@udecode/plate-common'; export interface TSlashInputElement extends TElement {} diff --git a/packages/slate/src/interfaces/element/TElement.ts b/packages/slate/src/interfaces/element/TElement.ts index c801963638..e5b9d86ab5 100644 --- a/packages/slate/src/interfaces/element/TElement.ts +++ b/packages/slate/src/interfaces/element/TElement.ts @@ -1,28 +1,24 @@ -import { UnknownObject } from '@udecode/utils'; +import type { UnknownObject } from '@udecode/utils'; -import { TEditor, Value } from '../editor/TEditor'; -import { TDescendant } from '../node/TDescendant'; -import { TNode } from '../node/TNode'; -import { EText } from '../text/TText'; +import type { TEditor, Value } from '../editor/TEditor'; +import type { TDescendant } from '../node/TDescendant'; +import type { TNode } from '../node/TNode'; +import type { EText } from '../text/TText'; /** * `Element` objects are a type of node in a Slate document that contain other * element nodes or text nodes. They can be either "blocks" or "inlines" * depending on the Slate editor's configuration. */ -export type TElement = UnknownObject & { +export type TElement = { children: TDescendant[]; type: string; -}; +} & UnknownObject; -/** - * Element of an editor. - */ +/** Element of an editor. */ export type EElement = ElementOf>; -/** - * Element or text of an editor. Differs from EDescendant. - */ +/** Element or text of an editor. Differs from EDescendant. */ export type EElementOrText = EElement | EText; /** @@ -31,20 +27,18 @@ export type EElementOrText = EElement | EText; */ // export type TElementEntry = [TElement, Path]; -/** - * A utility type to get all the element nodes type from a root node. - */ +/** A utility type to get all the element nodes type from a root node. */ export type ElementOf = TEditor extends N ? TElement : TElement extends N ? TElement : N extends TEditor ? - | Extract | ElementOf + | Extract : N extends TElement ? - | N - | Extract | ElementOf + | Extract + | N : never; diff --git a/packages/slate/src/interfaces/node/TAncestor.ts b/packages/slate/src/interfaces/node/TAncestor.ts index 6cde17aef9..05e3b10b11 100644 --- a/packages/slate/src/interfaces/node/TAncestor.ts +++ b/packages/slate/src/interfaces/node/TAncestor.ts @@ -1,28 +1,24 @@ -import { TEditor, Value } from '../editor/TEditor'; -import { ElementOf, TElement } from '../element/TElement'; -import { TNode } from './TNode'; +import type { TEditor, Value } from '../editor/TEditor'; +import type { ElementOf, TElement } from '../element/TElement'; +import type { TNode } from './TNode'; /** - * The `Ancestor` union type represents nodes that are ancestors in the tree. - * It is returned as a convenience in certain cases to narrow a value further - * than the more generic `Node` union. + * The `Ancestor` union type represents nodes that are ancestors in the tree. It + * is returned as a convenience in certain cases to narrow a value further than + * the more generic `Node` union. */ export type TAncestor = TEditor | TElement; -/** - * Ancestor of an editor. - */ +/** Ancestor of an editor. */ export type EAncestor = AncestorOf>; -/** - * A utility type to get all the ancestor node types from a root node type. - */ +/** A utility type to get all the ancestor node types from a root node type. */ export type AncestorOf = TEditor extends N ? TEditor | TElement : TElement extends N ? TElement : N extends TEditor - ? N | N['children'][number] | ElementOf + ? ElementOf | N | N['children'][number] : N extends TElement - ? N | ElementOf + ? ElementOf | N : never; diff --git a/yarn.lock b/yarn.lock index a82967a9b8..d0ef4d48ea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5582,7 +5582,7 @@ __metadata: version: 0.0.0-use.local resolution: "@udecode/cn@workspace:packages/cn" dependencies: - "@udecode/react-utils": "npm:31.0.0" + "@udecode/react-utils": "npm:33.0.0" peerDependencies: class-variance-authority: ">=0.7.0" react: ">=16.8.0" @@ -5591,13 +5591,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-alignment@npm:32.0.0, @udecode/plate-alignment@workspace:^, @udecode/plate-alignment@workspace:packages/alignment": +"@udecode/plate-alignment@npm:33.0.0, @udecode/plate-alignment@workspace:^, @udecode/plate-alignment@workspace:packages/alignment": version: 0.0.0-use.local resolution: "@udecode/plate-alignment@workspace:packages/alignment" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5607,14 +5607,14 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-autoformat@npm:32.0.0, @udecode/plate-autoformat@workspace:^, @udecode/plate-autoformat@workspace:packages/autoformat": +"@udecode/plate-autoformat@npm:33.0.0, @udecode/plate-autoformat@workspace:^, @udecode/plate-autoformat@workspace:packages/autoformat": version: 0.0.0-use.local resolution: "@udecode/plate-autoformat@workspace:packages/autoformat" dependencies: "@udecode/plate-common": "workspace:^" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5624,17 +5624,17 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-basic-elements@npm:32.0.0, @udecode/plate-basic-elements@workspace:^, @udecode/plate-basic-elements@workspace:packages/basic-elements": +"@udecode/plate-basic-elements@npm:33.0.0, @udecode/plate-basic-elements@workspace:^, @udecode/plate-basic-elements@workspace:packages/basic-elements": version: 0.0.0-use.local resolution: "@udecode/plate-basic-elements@workspace:packages/basic-elements" dependencies: - "@udecode/plate-block-quote": "npm:32.0.0" - "@udecode/plate-code-block": "npm:32.0.0" + "@udecode/plate-block-quote": "npm:33.0.0" + "@udecode/plate-code-block": "npm:33.0.0" "@udecode/plate-common": "workspace:^" - "@udecode/plate-heading": "npm:32.0.0" - "@udecode/plate-paragraph": "npm:32.0.0" + "@udecode/plate-heading": "npm:33.0.0" + "@udecode/plate-paragraph": "npm:33.0.0" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5644,13 +5644,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-basic-marks@npm:32.0.0, @udecode/plate-basic-marks@workspace:^, @udecode/plate-basic-marks@workspace:packages/basic-marks": +"@udecode/plate-basic-marks@npm:33.0.0, @udecode/plate-basic-marks@workspace:^, @udecode/plate-basic-marks@workspace:packages/basic-marks": version: 0.0.0-use.local resolution: "@udecode/plate-basic-marks@workspace:packages/basic-marks" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5660,13 +5660,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-block-quote@npm:32.0.0, @udecode/plate-block-quote@workspace:^, @udecode/plate-block-quote@workspace:packages/block-quote": +"@udecode/plate-block-quote@npm:33.0.0, @udecode/plate-block-quote@workspace:^, @udecode/plate-block-quote@workspace:packages/block-quote": version: 0.0.0-use.local resolution: "@udecode/plate-block-quote@workspace:packages/block-quote" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5676,13 +5676,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-break@npm:32.0.0, @udecode/plate-break@workspace:^, @udecode/plate-break@workspace:packages/break": +"@udecode/plate-break@npm:33.0.0, @udecode/plate-break@workspace:^, @udecode/plate-break@workspace:packages/break": version: 0.0.0-use.local resolution: "@udecode/plate-break@workspace:packages/break" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5699,7 +5699,7 @@ __metadata: "@udecode/plate-common": "workspace:^" react-textarea-autosize: "npm:^8.5.3" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5718,7 +5718,7 @@ __metadata: delay: "npm:5.0.0" p-defer: "npm:^3.0.0" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5728,14 +5728,14 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-code-block@npm:32.0.0, @udecode/plate-code-block@workspace:^, @udecode/plate-code-block@workspace:packages/code-block": +"@udecode/plate-code-block@npm:33.0.0, @udecode/plate-code-block@workspace:^, @udecode/plate-code-block@workspace:packages/code-block": version: 0.0.0-use.local resolution: "@udecode/plate-code-block@workspace:packages/code-block" dependencies: "@udecode/plate-common": "workspace:^" prismjs: "npm:^1.29.0" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5745,14 +5745,14 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-combobox@npm:32.0.0, @udecode/plate-combobox@workspace:^, @udecode/plate-combobox@workspace:packages/combobox": +"@udecode/plate-combobox@npm:33.0.0, @udecode/plate-combobox@workspace:^, @udecode/plate-combobox@workspace:packages/combobox": version: 0.0.0-use.local resolution: "@udecode/plate-combobox@workspace:packages/combobox" dependencies: "@udecode/plate-common": "workspace:^" downshift: "npm:^6.1.12" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5762,14 +5762,14 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-comments@npm:32.0.0, @udecode/plate-comments@workspace:^, @udecode/plate-comments@workspace:packages/comments": +"@udecode/plate-comments@npm:33.0.0, @udecode/plate-comments@workspace:^, @udecode/plate-comments@workspace:packages/comments": version: 0.0.0-use.local resolution: "@udecode/plate-comments@workspace:packages/comments" dependencies: "@udecode/plate-common": "workspace:^" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5779,15 +5779,15 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-common@npm:32.0.1, @udecode/plate-common@workspace:^, @udecode/plate-common@workspace:packages/common": +"@udecode/plate-common@npm:33.0.0, @udecode/plate-common@workspace:^, @udecode/plate-common@workspace:packages/common": version: 0.0.0-use.local resolution: "@udecode/plate-common@workspace:packages/common" dependencies: - "@udecode/plate-core": "npm:32.0.1" - "@udecode/plate-utils": "npm:32.0.1" - "@udecode/react-utils": "npm:31.0.0" + "@udecode/plate-core": "npm:33.0.0" + "@udecode/plate-utils": "npm:33.0.0" + "@udecode/react-utils": "npm:33.0.0" "@udecode/slate": "npm:32.0.1" - "@udecode/slate-react": "npm:32.0.1" + "@udecode/slate-react": "npm:33.0.0" "@udecode/slate-utils": "npm:32.0.1" "@udecode/utils": "npm:31.0.0" peerDependencies: @@ -5800,12 +5800,12 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-core@npm:32.0.1, @udecode/plate-core@workspace:^, @udecode/plate-core@workspace:packages/core": +"@udecode/plate-core@npm:33.0.0, @udecode/plate-core@workspace:^, @udecode/plate-core@workspace:packages/core": version: 0.0.0-use.local resolution: "@udecode/plate-core@workspace:packages/core" dependencies: "@udecode/slate": "npm:32.0.1" - "@udecode/slate-react": "npm:32.0.1" + "@udecode/slate-react": "npm:33.0.0" "@udecode/slate-utils": "npm:32.0.1" "@udecode/utils": "npm:31.0.0" clsx: "npm:^1.2.1" @@ -5836,7 +5836,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5846,14 +5846,14 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-diff@npm:32.0.0, @udecode/plate-diff@workspace:^, @udecode/plate-diff@workspace:packages/diff": +"@udecode/plate-diff@npm:33.0.0, @udecode/plate-diff@workspace:^, @udecode/plate-diff@workspace:packages/diff": version: 0.0.0-use.local resolution: "@udecode/plate-diff@workspace:packages/diff" dependencies: "@udecode/plate-common": "workspace:^" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5871,7 +5871,7 @@ __metadata: lodash: "npm:^4.17.21" raf: "npm:^3.4.1" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dnd: ">=14.0.0" react-dnd-html5-backend: ">=14.0.0" @@ -5888,10 +5888,10 @@ __metadata: resolution: "@udecode/plate-emoji@workspace:packages/emoji" dependencies: "@emoji-mart/data": "npm:^1.1.2" - "@udecode/plate-combobox": "npm:32.0.0" + "@udecode/plate-combobox": "npm:33.0.0" "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5908,7 +5908,7 @@ __metadata: "@excalidraw/excalidraw": "npm:0.16.4" "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5918,13 +5918,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-find-replace@npm:32.0.0, @udecode/plate-find-replace@workspace:^, @udecode/plate-find-replace@workspace:packages/find-replace": +"@udecode/plate-find-replace@npm:33.0.0, @udecode/plate-find-replace@workspace:^, @udecode/plate-find-replace@workspace:packages/find-replace": version: 0.0.0-use.local resolution: "@udecode/plate-find-replace@workspace:packages/find-replace" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5934,7 +5934,7 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-floating@npm:32.0.0, @udecode/plate-floating@workspace:^, @udecode/plate-floating@workspace:packages/floating": +"@udecode/plate-floating@npm:33.0.0, @udecode/plate-floating@workspace:^, @udecode/plate-floating@workspace:packages/floating": version: 0.0.0-use.local resolution: "@udecode/plate-floating@workspace:packages/floating" dependencies: @@ -5942,7 +5942,7 @@ __metadata: "@floating-ui/react": "npm:^0.22.3" "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5952,14 +5952,14 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-font@npm:32.0.0, @udecode/plate-font@workspace:^, @udecode/plate-font@workspace:packages/font": +"@udecode/plate-font@npm:33.0.0, @udecode/plate-font@workspace:^, @udecode/plate-font@workspace:packages/font": version: 0.0.0-use.local resolution: "@udecode/plate-font@workspace:packages/font" dependencies: "@udecode/plate-common": "workspace:^" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5969,13 +5969,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-heading@npm:32.0.0, @udecode/plate-heading@workspace:^, @udecode/plate-heading@workspace:packages/heading": +"@udecode/plate-heading@npm:33.0.0, @udecode/plate-heading@workspace:^, @udecode/plate-heading@workspace:packages/heading": version: 0.0.0-use.local resolution: "@udecode/plate-heading@workspace:packages/heading" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -5985,13 +5985,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-highlight@npm:32.0.0, @udecode/plate-highlight@workspace:^, @udecode/plate-highlight@workspace:packages/highlight": +"@udecode/plate-highlight@npm:33.0.0, @udecode/plate-highlight@workspace:^, @udecode/plate-highlight@workspace:packages/highlight": version: 0.0.0-use.local resolution: "@udecode/plate-highlight@workspace:packages/highlight" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6001,13 +6001,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-horizontal-rule@npm:32.0.0, @udecode/plate-horizontal-rule@workspace:^, @udecode/plate-horizontal-rule@workspace:packages/horizontal-rule": +"@udecode/plate-horizontal-rule@npm:33.0.0, @udecode/plate-horizontal-rule@workspace:^, @udecode/plate-horizontal-rule@workspace:packages/horizontal-rule": version: 0.0.0-use.local resolution: "@udecode/plate-horizontal-rule@workspace:packages/horizontal-rule" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6017,16 +6017,16 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-indent-list@npm:32.0.1, @udecode/plate-indent-list@workspace:^, @udecode/plate-indent-list@workspace:packages/indent-list": +"@udecode/plate-indent-list@npm:33.0.0, @udecode/plate-indent-list@workspace:^, @udecode/plate-indent-list@workspace:packages/indent-list": version: 0.0.0-use.local resolution: "@udecode/plate-indent-list@workspace:packages/indent-list" dependencies: "@udecode/plate-common": "workspace:^" - "@udecode/plate-indent": "npm:32.0.0" - "@udecode/plate-list": "npm:32.0.0" + "@udecode/plate-indent": "npm:33.0.0" + "@udecode/plate-list": "npm:33.0.0" clsx: "npm:^1.2.1" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6036,13 +6036,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-indent@npm:32.0.0, @udecode/plate-indent@workspace:^, @udecode/plate-indent@workspace:packages/indent": +"@udecode/plate-indent@npm:33.0.0, @udecode/plate-indent@workspace:^, @udecode/plate-indent@workspace:packages/indent": version: 0.0.0-use.local resolution: "@udecode/plate-indent@workspace:packages/indent" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6059,7 +6059,7 @@ __metadata: "@udecode/plate-common": "workspace:^" juice: "npm:^8.1.0" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6069,13 +6069,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-kbd@npm:32.0.0, @udecode/plate-kbd@workspace:^, @udecode/plate-kbd@workspace:packages/kbd": +"@udecode/plate-kbd@npm:33.0.0, @udecode/plate-kbd@workspace:^, @udecode/plate-kbd@workspace:packages/kbd": version: 0.0.0-use.local resolution: "@udecode/plate-kbd@workspace:packages/kbd" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6091,7 +6091,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6101,13 +6101,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-line-height@npm:32.0.0, @udecode/plate-line-height@workspace:^, @udecode/plate-line-height@workspace:packages/line-height": +"@udecode/plate-line-height@npm:33.0.0, @udecode/plate-line-height@workspace:^, @udecode/plate-line-height@workspace:packages/line-height": version: 0.0.0-use.local resolution: "@udecode/plate-line-height@workspace:packages/line-height" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6117,15 +6117,15 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-link@npm:32.0.0, @udecode/plate-link@workspace:^, @udecode/plate-link@workspace:packages/link": +"@udecode/plate-link@npm:33.0.0, @udecode/plate-link@workspace:^, @udecode/plate-link@workspace:packages/link": version: 0.0.0-use.local resolution: "@udecode/plate-link@workspace:packages/link" dependencies: "@udecode/plate-common": "workspace:^" - "@udecode/plate-floating": "npm:32.0.0" - "@udecode/plate-normalizers": "npm:32.0.0" + "@udecode/plate-floating": "npm:33.0.0" + "@udecode/plate-normalizers": "npm:33.0.0" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6135,15 +6135,15 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-list@npm:32.0.0, @udecode/plate-list@workspace:^, @udecode/plate-list@workspace:packages/list": +"@udecode/plate-list@npm:33.0.0, @udecode/plate-list@workspace:^, @udecode/plate-list@workspace:packages/list": version: 0.0.0-use.local resolution: "@udecode/plate-list@workspace:packages/list" dependencies: "@udecode/plate-common": "workspace:^" - "@udecode/plate-reset-node": "npm:32.0.0" + "@udecode/plate-reset-node": "npm:33.0.0" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6153,14 +6153,14 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-media@npm:32.0.0, @udecode/plate-media@workspace:^, @udecode/plate-media@workspace:packages/media": +"@udecode/plate-media@npm:33.0.0, @udecode/plate-media@workspace:^, @udecode/plate-media@workspace:packages/media": version: 0.0.0-use.local resolution: "@udecode/plate-media@workspace:packages/media" dependencies: "@udecode/plate-common": "workspace:^" js-video-url-parser: "npm:^0.5.1" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6170,14 +6170,14 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-mention@npm:32.0.0, @udecode/plate-mention@workspace:^, @udecode/plate-mention@workspace:packages/mention": +"@udecode/plate-mention@npm:33.0.0, @udecode/plate-mention@workspace:^, @udecode/plate-mention@workspace:packages/mention": version: 0.0.0-use.local resolution: "@udecode/plate-mention@workspace:packages/mention" dependencies: - "@udecode/plate-combobox": "npm:32.0.0" + "@udecode/plate-combobox": "npm:33.0.0" "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6187,14 +6187,14 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-node-id@npm:32.0.0, @udecode/plate-node-id@workspace:^, @udecode/plate-node-id@workspace:packages/node-id": +"@udecode/plate-node-id@npm:33.0.0, @udecode/plate-node-id@workspace:^, @udecode/plate-node-id@workspace:packages/node-id": version: 0.0.0-use.local resolution: "@udecode/plate-node-id@workspace:packages/node-id" dependencies: "@udecode/plate-common": "workspace:^" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6204,14 +6204,14 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-normalizers@npm:32.0.0, @udecode/plate-normalizers@workspace:^, @udecode/plate-normalizers@workspace:packages/normalizers": +"@udecode/plate-normalizers@npm:33.0.0, @udecode/plate-normalizers@workspace:^, @udecode/plate-normalizers@workspace:packages/normalizers": version: 0.0.0-use.local resolution: "@udecode/plate-normalizers@workspace:packages/normalizers" dependencies: "@udecode/plate-common": "workspace:^" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6221,13 +6221,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-paragraph@npm:32.0.0, @udecode/plate-paragraph@workspace:^, @udecode/plate-paragraph@workspace:packages/paragraph": +"@udecode/plate-paragraph@npm:33.0.0, @udecode/plate-paragraph@workspace:^, @udecode/plate-paragraph@workspace:packages/paragraph": version: 0.0.0-use.local resolution: "@udecode/plate-paragraph@workspace:packages/paragraph" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6237,13 +6237,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-reset-node@npm:32.0.0, @udecode/plate-reset-node@workspace:^, @udecode/plate-reset-node@workspace:packages/reset-node": +"@udecode/plate-reset-node@npm:33.0.0, @udecode/plate-reset-node@workspace:^, @udecode/plate-reset-node@workspace:packages/reset-node": version: 0.0.0-use.local resolution: "@udecode/plate-reset-node@workspace:packages/reset-node" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6253,13 +6253,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-resizable@npm:32.0.0, @udecode/plate-resizable@workspace:^, @udecode/plate-resizable@workspace:packages/resizable": +"@udecode/plate-resizable@npm:33.0.0, @udecode/plate-resizable@workspace:^, @udecode/plate-resizable@workspace:packages/resizable": version: 0.0.0-use.local resolution: "@udecode/plate-resizable@workspace:packages/resizable" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6269,13 +6269,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-select@npm:32.0.0, @udecode/plate-select@workspace:^, @udecode/plate-select@workspace:packages/select": +"@udecode/plate-select@npm:33.0.0, @udecode/plate-select@workspace:^, @udecode/plate-select@workspace:packages/select": version: 0.0.0-use.local resolution: "@udecode/plate-select@workspace:packages/select" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6293,7 +6293,7 @@ __metadata: "@viselect/vanilla": "npm:3.2.5" copy-to-clipboard: "npm:^3.3.3" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6303,15 +6303,16 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-serializer-csv@npm:32.0.0, @udecode/plate-serializer-csv@workspace:^, @udecode/plate-serializer-csv@workspace:packages/serializer-csv": +"@udecode/plate-serializer-csv@npm:33.0.0, @udecode/plate-serializer-csv@workspace:^, @udecode/plate-serializer-csv@workspace:packages/serializer-csv": version: 0.0.0-use.local resolution: "@udecode/plate-serializer-csv@workspace:packages/serializer-csv" dependencies: "@types/papaparse": "npm:^5.3.14" "@udecode/plate-common": "workspace:^" + "@udecode/plate-table": "npm:33.0.0" papaparse: "npm:^5.4.1" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6321,14 +6322,20 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-serializer-docx@npm:32.0.1, @udecode/plate-serializer-docx@workspace:^, @udecode/plate-serializer-docx@workspace:packages/serializer-docx": +"@udecode/plate-serializer-docx@npm:33.0.0, @udecode/plate-serializer-docx@workspace:^, @udecode/plate-serializer-docx@workspace:packages/serializer-docx": version: 0.0.0-use.local resolution: "@udecode/plate-serializer-docx@workspace:packages/serializer-docx" dependencies: "@udecode/plate-common": "workspace:^" + "@udecode/plate-heading": "npm:33.0.0" + "@udecode/plate-indent": "npm:33.0.0" + "@udecode/plate-indent-list": "npm:33.0.0" + "@udecode/plate-media": "npm:33.0.0" + "@udecode/plate-paragraph": "npm:33.0.0" + "@udecode/plate-table": "npm:33.0.0" validator: "npm:^13.11.0" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6338,7 +6345,7 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-serializer-html@npm:32.0.0, @udecode/plate-serializer-html@workspace:^, @udecode/plate-serializer-html@workspace:packages/serializer-html": +"@udecode/plate-serializer-html@npm:33.0.0, @udecode/plate-serializer-html@workspace:^, @udecode/plate-serializer-html@workspace:packages/serializer-html": version: 0.0.0-use.local resolution: "@udecode/plate-serializer-html@workspace:packages/serializer-html" dependencies: @@ -6346,7 +6353,7 @@ __metadata: "@udecode/plate-common": "workspace:^" html-entities: "npm:^2.5.2" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6356,7 +6363,7 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-serializer-md@npm:32.0.0, @udecode/plate-serializer-md@workspace:^, @udecode/plate-serializer-md@workspace:packages/serializer-md": +"@udecode/plate-serializer-md@npm:33.0.0, @udecode/plate-serializer-md@workspace:^, @udecode/plate-serializer-md@workspace:packages/serializer-md": version: 0.0.0-use.local resolution: "@udecode/plate-serializer-md@workspace:packages/serializer-md" dependencies: @@ -6365,7 +6372,7 @@ __metadata: remark-parse: "npm:^9.0.0" unified: "npm:^9.2.2" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6379,10 +6386,10 @@ __metadata: version: 0.0.0-use.local resolution: "@udecode/plate-slash-command@workspace:packages/slash-command" dependencies: - "@udecode/plate-combobox": "npm:32.0.0" + "@udecode/plate-combobox": "npm:33.0.0" "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6392,15 +6399,15 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-suggestion@npm:32.0.0, @udecode/plate-suggestion@workspace:^, @udecode/plate-suggestion@workspace:packages/suggestion": +"@udecode/plate-suggestion@npm:33.0.0, @udecode/plate-suggestion@workspace:^, @udecode/plate-suggestion@workspace:packages/suggestion": version: 0.0.0-use.local resolution: "@udecode/plate-suggestion@workspace:packages/suggestion" dependencies: "@udecode/plate-common": "workspace:^" - "@udecode/plate-diff": "npm:32.0.0" + "@udecode/plate-diff": "npm:33.0.0" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6410,14 +6417,14 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-tabbable@npm:32.0.0, @udecode/plate-tabbable@workspace:^, @udecode/plate-tabbable@workspace:packages/tabbable": +"@udecode/plate-tabbable@npm:33.0.0, @udecode/plate-tabbable@workspace:^, @udecode/plate-tabbable@workspace:packages/tabbable": version: 0.0.0-use.local resolution: "@udecode/plate-tabbable@workspace:packages/tabbable" dependencies: "@udecode/plate-common": "workspace:^" tabbable: "npm:^6.2.0" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6427,15 +6434,15 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-table@npm:32.0.0, @udecode/plate-table@workspace:^, @udecode/plate-table@workspace:packages/table": +"@udecode/plate-table@npm:33.0.0, @udecode/plate-table@workspace:^, @udecode/plate-table@workspace:packages/table": version: 0.0.0-use.local resolution: "@udecode/plate-table@workspace:packages/table" dependencies: "@udecode/plate-common": "workspace:^" - "@udecode/plate-resizable": "npm:32.0.0" + "@udecode/plate-resizable": "npm:33.0.0" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6453,16 +6460,16 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-toggle@npm:32.0.0, @udecode/plate-toggle@workspace:^, @udecode/plate-toggle@workspace:packages/toggle": +"@udecode/plate-toggle@npm:33.0.0, @udecode/plate-toggle@workspace:^, @udecode/plate-toggle@workspace:packages/toggle": version: 0.0.0-use.local resolution: "@udecode/plate-toggle@workspace:packages/toggle" dependencies: "@udecode/plate-common": "workspace:^" - "@udecode/plate-indent": "npm:32.0.0" - "@udecode/plate-node-id": "npm:32.0.0" + "@udecode/plate-indent": "npm:33.0.0" + "@udecode/plate-node-id": "npm:33.0.0" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6472,13 +6479,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-trailing-block@npm:32.0.0, @udecode/plate-trailing-block@workspace:^, @udecode/plate-trailing-block@workspace:packages/trailing-block": +"@udecode/plate-trailing-block@npm:33.0.0, @udecode/plate-trailing-block@workspace:^, @udecode/plate-trailing-block@workspace:packages/trailing-block": version: 0.0.0-use.local resolution: "@udecode/plate-trailing-block@workspace:packages/trailing-block" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6519,14 +6526,14 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-utils@npm:32.0.1, @udecode/plate-utils@workspace:^, @udecode/plate-utils@workspace:packages/plate-utils": +"@udecode/plate-utils@npm:33.0.0, @udecode/plate-utils@workspace:^, @udecode/plate-utils@workspace:packages/plate-utils": version: 0.0.0-use.local resolution: "@udecode/plate-utils@workspace:packages/plate-utils" dependencies: - "@udecode/plate-core": "npm:32.0.1" - "@udecode/react-utils": "npm:31.0.0" + "@udecode/plate-core": "npm:33.0.0" + "@udecode/react-utils": "npm:33.0.0" "@udecode/slate": "npm:32.0.1" - "@udecode/slate-react": "npm:32.0.1" + "@udecode/slate-react": "npm:33.0.0" "@udecode/slate-utils": "npm:32.0.1" "@udecode/utils": "npm:31.0.0" clsx: "npm:^1.2.1" @@ -6550,7 +6557,7 @@ __metadata: "@udecode/plate-common": "workspace:^" yjs: "npm:^13.6.14" peerDependencies: - "@udecode/plate-common": ">=32.0.1" + "@udecode/plate-common": ">=33.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6564,46 +6571,46 @@ __metadata: version: 0.0.0-use.local resolution: "@udecode/plate@workspace:packages/plate" dependencies: - "@udecode/plate-alignment": "npm:32.0.0" - "@udecode/plate-autoformat": "npm:32.0.0" - "@udecode/plate-basic-elements": "npm:32.0.0" - "@udecode/plate-basic-marks": "npm:32.0.0" - "@udecode/plate-block-quote": "npm:32.0.0" - "@udecode/plate-break": "npm:32.0.0" - "@udecode/plate-code-block": "npm:32.0.0" - "@udecode/plate-combobox": "npm:32.0.0" - "@udecode/plate-comments": "npm:32.0.0" - "@udecode/plate-common": "npm:32.0.1" - "@udecode/plate-diff": "npm:32.0.0" - "@udecode/plate-find-replace": "npm:32.0.0" - "@udecode/plate-floating": "npm:32.0.0" - "@udecode/plate-font": "npm:32.0.0" - "@udecode/plate-heading": "npm:32.0.0" - "@udecode/plate-highlight": "npm:32.0.0" - "@udecode/plate-horizontal-rule": "npm:32.0.0" - "@udecode/plate-indent": "npm:32.0.0" - "@udecode/plate-indent-list": "npm:32.0.1" - "@udecode/plate-kbd": "npm:32.0.0" - "@udecode/plate-line-height": "npm:32.0.0" - "@udecode/plate-link": "npm:32.0.0" - "@udecode/plate-list": "npm:32.0.0" - "@udecode/plate-media": "npm:32.0.0" - "@udecode/plate-mention": "npm:32.0.0" - "@udecode/plate-node-id": "npm:32.0.0" - "@udecode/plate-normalizers": "npm:32.0.0" - "@udecode/plate-paragraph": "npm:32.0.0" - "@udecode/plate-reset-node": "npm:32.0.0" - "@udecode/plate-resizable": "npm:32.0.0" - "@udecode/plate-select": "npm:32.0.0" - "@udecode/plate-serializer-csv": "npm:32.0.0" - "@udecode/plate-serializer-docx": "npm:32.0.1" - "@udecode/plate-serializer-html": "npm:32.0.0" - "@udecode/plate-serializer-md": "npm:32.0.0" - "@udecode/plate-suggestion": "npm:32.0.0" - "@udecode/plate-tabbable": "npm:32.0.0" - "@udecode/plate-table": "npm:32.0.0" - "@udecode/plate-toggle": "npm:32.0.0" - "@udecode/plate-trailing-block": "npm:32.0.0" + "@udecode/plate-alignment": "npm:33.0.0" + "@udecode/plate-autoformat": "npm:33.0.0" + "@udecode/plate-basic-elements": "npm:33.0.0" + "@udecode/plate-basic-marks": "npm:33.0.0" + "@udecode/plate-block-quote": "npm:33.0.0" + "@udecode/plate-break": "npm:33.0.0" + "@udecode/plate-code-block": "npm:33.0.0" + "@udecode/plate-combobox": "npm:33.0.0" + "@udecode/plate-comments": "npm:33.0.0" + "@udecode/plate-common": "npm:33.0.0" + "@udecode/plate-diff": "npm:33.0.0" + "@udecode/plate-find-replace": "npm:33.0.0" + "@udecode/plate-floating": "npm:33.0.0" + "@udecode/plate-font": "npm:33.0.0" + "@udecode/plate-heading": "npm:33.0.0" + "@udecode/plate-highlight": "npm:33.0.0" + "@udecode/plate-horizontal-rule": "npm:33.0.0" + "@udecode/plate-indent": "npm:33.0.0" + "@udecode/plate-indent-list": "npm:33.0.0" + "@udecode/plate-kbd": "npm:33.0.0" + "@udecode/plate-line-height": "npm:33.0.0" + "@udecode/plate-link": "npm:33.0.0" + "@udecode/plate-list": "npm:33.0.0" + "@udecode/plate-media": "npm:33.0.0" + "@udecode/plate-mention": "npm:33.0.0" + "@udecode/plate-node-id": "npm:33.0.0" + "@udecode/plate-normalizers": "npm:33.0.0" + "@udecode/plate-paragraph": "npm:33.0.0" + "@udecode/plate-reset-node": "npm:33.0.0" + "@udecode/plate-resizable": "npm:33.0.0" + "@udecode/plate-select": "npm:33.0.0" + "@udecode/plate-serializer-csv": "npm:33.0.0" + "@udecode/plate-serializer-docx": "npm:33.0.0" + "@udecode/plate-serializer-html": "npm:33.0.0" + "@udecode/plate-serializer-md": "npm:33.0.0" + "@udecode/plate-suggestion": "npm:33.0.0" + "@udecode/plate-tabbable": "npm:33.0.0" + "@udecode/plate-table": "npm:33.0.0" + "@udecode/plate-toggle": "npm:33.0.0" + "@udecode/plate-trailing-block": "npm:33.0.0" peerDependencies: react: ">=16.8.0" react-dom: ">=16.8.0" @@ -6614,7 +6621,7 @@ __metadata: languageName: unknown linkType: soft -"@udecode/react-utils@npm:31.0.0, @udecode/react-utils@workspace:^, @udecode/react-utils@workspace:packages/react-utils": +"@udecode/react-utils@npm:33.0.0, @udecode/react-utils@workspace:^, @udecode/react-utils@workspace:packages/react-utils": version: 0.0.0-use.local resolution: "@udecode/react-utils@workspace:packages/react-utils" dependencies: @@ -6627,11 +6634,11 @@ __metadata: languageName: unknown linkType: soft -"@udecode/slate-react@npm:32.0.1, @udecode/slate-react@workspace:^, @udecode/slate-react@workspace:packages/slate-react": +"@udecode/slate-react@npm:33.0.0, @udecode/slate-react@workspace:^, @udecode/slate-react@workspace:packages/slate-react": version: 0.0.0-use.local resolution: "@udecode/slate-react@workspace:packages/slate-react" dependencies: - "@udecode/react-utils": "npm:31.0.0" + "@udecode/react-utils": "npm:33.0.0" "@udecode/slate": "npm:32.0.1" "@udecode/utils": "npm:31.0.0" peerDependencies: @@ -18846,17 +18853,6 @@ __metadata: languageName: node linkType: hard -"slate@npm:0.102.0": - version: 0.102.0 - resolution: "slate@npm:0.102.0" - dependencies: - immer: "npm:^10.0.3" - is-plain-object: "npm:^5.0.0" - tiny-warning: "npm:^1.0.3" - checksum: 10c0/cce8271c36228c16b2512ce48b1aac6a00b33aa24771ee925d6eed4c20d19b82a45125f70be161f3d51fc887b7a8f560885a1c438d71abf0acd13a3d42a87a03 - languageName: node - linkType: hard - "slate@npm:0.103.0": version: 0.103.0 resolution: "slate@npm:0.103.0" @@ -21736,7 +21732,7 @@ __metadata: rimraf: "npm:^5.0.5" sass: "npm:^1.72.0" shiki: "npm:^0.12.1" - slate: "npm:0.102.0" + slate: "npm:0.103.0" slate-history: "npm:0.100.0" slate-hyperscript: "npm:0.100.0" slate-react: "npm:0.102.0" From 751a9bb581afa4b68c658e35b0c041f45ee9855e Mon Sep 17 00:00:00 2001 From: zbeyens Date: Mon, 6 May 2024 20:38:28 +0200 Subject: [PATCH 30/63] eslint --- packages/combobox/src/hooks/useComboboxInput.ts | 4 +--- .../combobox/src/legacy-combobox-delete-me/combobox.store.ts | 2 +- .../src/legacy-combobox-delete-me/createComboboxPlugin.ts | 2 +- .../src/legacy-combobox-delete-me/onChangeCombobox.ts | 2 +- .../src/legacy-combobox-delete-me/onKeyDownCombobox.ts | 4 ++-- .../legacy-combobox-delete-me/types/ComboboxOnSelectItem.ts | 2 +- .../src/legacy-combobox-delete-me/utils/getTextFromTrigger.ts | 2 +- packages/combobox/src/types.ts | 2 +- packages/combobox/src/withTriggerCombobox.ts | 2 +- packages/mention/src/createMentionPlugin.ts | 2 +- packages/mention/src/getMentionOnSelectItem.ts | 2 +- packages/mention/src/types.ts | 2 +- packages/slash-command/src/createSlashPlugin.ts | 2 +- packages/slash-command/src/types.ts | 2 +- 14 files changed, 15 insertions(+), 17 deletions(-) diff --git a/packages/combobox/src/hooks/useComboboxInput.ts b/packages/combobox/src/hooks/useComboboxInput.ts index f135e54119..92a81f747e 100644 --- a/packages/combobox/src/hooks/useComboboxInput.ts +++ b/packages/combobox/src/hooks/useComboboxInput.ts @@ -7,14 +7,12 @@ import { } from 'react'; import { - Hotkeys, findNodePath, focusEditor, - isHotkey, - removeNodes, useEditorRef, useElement, } from '@udecode/plate-common'; +import { Hotkeys, isHotkey, removeNodes } from '@udecode/plate-common/server'; import { useSelected } from 'slate-react'; import type { diff --git a/packages/combobox/src/legacy-combobox-delete-me/combobox.store.ts b/packages/combobox/src/legacy-combobox-delete-me/combobox.store.ts index b0c91c52dd..67ffa69b85 100644 --- a/packages/combobox/src/legacy-combobox-delete-me/combobox.store.ts +++ b/packages/combobox/src/legacy-combobox-delete-me/combobox.store.ts @@ -4,7 +4,7 @@ import { type ZustandStateActions, type ZustandStoreApi, createZustandStore, -} from '@udecode/plate-common'; +} from '@udecode/plate-common/server'; import type { ComboboxOnSelectItem, NoData, TComboboxItem } from './types'; diff --git a/packages/combobox/src/legacy-combobox-delete-me/createComboboxPlugin.ts b/packages/combobox/src/legacy-combobox-delete-me/createComboboxPlugin.ts index edf0155576..327dffca5c 100644 --- a/packages/combobox/src/legacy-combobox-delete-me/createComboboxPlugin.ts +++ b/packages/combobox/src/legacy-combobox-delete-me/createComboboxPlugin.ts @@ -1,4 +1,4 @@ -import { createPluginFactory } from '@udecode/plate-common'; +import { createPluginFactory } from '@udecode/plate-common/server'; import { onChangeCombobox } from './onChangeCombobox'; import { onKeyDownCombobox } from './onKeyDownCombobox'; diff --git a/packages/combobox/src/legacy-combobox-delete-me/onChangeCombobox.ts b/packages/combobox/src/legacy-combobox-delete-me/onChangeCombobox.ts index 876b405941..e58a45f01b 100644 --- a/packages/combobox/src/legacy-combobox-delete-me/onChangeCombobox.ts +++ b/packages/combobox/src/legacy-combobox-delete-me/onChangeCombobox.ts @@ -2,7 +2,7 @@ import { type PlateEditor, type Value, isCollapsed, -} from '@udecode/plate-common'; +} from '@udecode/plate-common/server'; import { Range } from 'slate'; import { comboboxActions, comboboxSelectors } from './combobox.store'; diff --git a/packages/combobox/src/legacy-combobox-delete-me/onKeyDownCombobox.ts b/packages/combobox/src/legacy-combobox-delete-me/onKeyDownCombobox.ts index f82a8a490e..a0770d9e7d 100644 --- a/packages/combobox/src/legacy-combobox-delete-me/onKeyDownCombobox.ts +++ b/packages/combobox/src/legacy-combobox-delete-me/onKeyDownCombobox.ts @@ -1,10 +1,10 @@ +import { Hotkeys } from '@udecode/plate-common'; import { - Hotkeys, type KeyboardHandlerReturnType, type PlateEditor, type Value, isHotkey, -} from '@udecode/plate-common'; +} from '@udecode/plate-common/server'; import { comboboxActions, diff --git a/packages/combobox/src/legacy-combobox-delete-me/types/ComboboxOnSelectItem.ts b/packages/combobox/src/legacy-combobox-delete-me/types/ComboboxOnSelectItem.ts index 6d00b046d1..362ffb07e1 100644 --- a/packages/combobox/src/legacy-combobox-delete-me/types/ComboboxOnSelectItem.ts +++ b/packages/combobox/src/legacy-combobox-delete-me/types/ComboboxOnSelectItem.ts @@ -1,4 +1,4 @@ -import type { PlateEditor, Value } from '@udecode/plate-common'; +import type { PlateEditor, Value } from '@udecode/plate-common/server'; export interface TComboboxItemBase { /** Unique key. */ diff --git a/packages/combobox/src/legacy-combobox-delete-me/utils/getTextFromTrigger.ts b/packages/combobox/src/legacy-combobox-delete-me/utils/getTextFromTrigger.ts index f62eaadcd6..2d73472d90 100644 --- a/packages/combobox/src/legacy-combobox-delete-me/utils/getTextFromTrigger.ts +++ b/packages/combobox/src/legacy-combobox-delete-me/utils/getTextFromTrigger.ts @@ -7,7 +7,7 @@ import { getEditorString, getPointBefore, getRange, -} from '@udecode/plate-common'; +} from '@udecode/plate-common/server'; /** * Get text and range from trigger to cursor. Starts with trigger and ends with diff --git a/packages/combobox/src/types.ts b/packages/combobox/src/types.ts index 5683f4ee42..b38b4f45a4 100644 --- a/packages/combobox/src/types.ts +++ b/packages/combobox/src/types.ts @@ -1,4 +1,4 @@ -import type { PlateEditor, TElement } from '@udecode/plate-common'; +import type { PlateEditor, TElement } from '@udecode/plate-common/server'; export interface TriggerComboboxPlugin { createComboboxInput?: (trigger: string) => TElement; diff --git a/packages/combobox/src/withTriggerCombobox.ts b/packages/combobox/src/withTriggerCombobox.ts index 47f905e10b..d112c4614e 100644 --- a/packages/combobox/src/withTriggerCombobox.ts +++ b/packages/combobox/src/withTriggerCombobox.ts @@ -6,7 +6,7 @@ import { getEditorString, getPointBefore, getRange, -} from '@udecode/plate-common'; +} from '@udecode/plate-common/server'; import type { TriggerComboboxPlugin } from './types'; diff --git a/packages/mention/src/createMentionPlugin.ts b/packages/mention/src/createMentionPlugin.ts index a3707bbc20..8be87914e2 100644 --- a/packages/mention/src/createMentionPlugin.ts +++ b/packages/mention/src/createMentionPlugin.ts @@ -1,5 +1,5 @@ import { withTriggerCombobox } from '@udecode/plate-combobox'; -import { createPluginFactory } from '@udecode/plate-common'; +import { createPluginFactory } from '@udecode/plate-common/server'; import type { MentionPlugin } from './types'; diff --git a/packages/mention/src/getMentionOnSelectItem.ts b/packages/mention/src/getMentionOnSelectItem.ts index 655808a877..9e5925011c 100644 --- a/packages/mention/src/getMentionOnSelectItem.ts +++ b/packages/mention/src/getMentionOnSelectItem.ts @@ -8,7 +8,7 @@ import { insertText, isEndPoint, moveSelection, -} from '@udecode/plate-common'; +} from '@udecode/plate-common/server'; import type { MentionPlugin, TMentionElement, TMentionItemBase } from './types'; diff --git a/packages/mention/src/types.ts b/packages/mention/src/types.ts index 3cfff08020..0a9dab1ce9 100644 --- a/packages/mention/src/types.ts +++ b/packages/mention/src/types.ts @@ -1,5 +1,5 @@ import type { TriggerComboboxPlugin } from '@udecode/plate-combobox'; -import type { TElement, TNodeProps } from '@udecode/plate-common'; +import type { TElement, TNodeProps } from '@udecode/plate-common/server'; export interface TMentionItemBase { text: string; diff --git a/packages/slash-command/src/createSlashPlugin.ts b/packages/slash-command/src/createSlashPlugin.ts index e9afd640d0..bd901b0468 100644 --- a/packages/slash-command/src/createSlashPlugin.ts +++ b/packages/slash-command/src/createSlashPlugin.ts @@ -2,7 +2,7 @@ import { type TriggerComboboxPlugin, withTriggerCombobox, } from '@udecode/plate-combobox'; -import { createPluginFactory } from '@udecode/plate-common'; +import { createPluginFactory } from '@udecode/plate-common/server'; export const KEY_SLASH_COMMAND = 'slash_command'; diff --git a/packages/slash-command/src/types.ts b/packages/slash-command/src/types.ts index 3c3e8de5ec..35f03537eb 100644 --- a/packages/slash-command/src/types.ts +++ b/packages/slash-command/src/types.ts @@ -1,3 +1,3 @@ -import type { TElement } from '@udecode/plate-common'; +import type { TElement } from '@udecode/plate-common/server'; export interface TSlashInputElement extends TElement {} From 904526e645a93454b0df2ef0bceba52e0b260111 Mon Sep 17 00:00:00 2001 From: Dzmitry Tamashevich Date: Tue, 28 May 2024 15:16:35 +0300 Subject: [PATCH 31/63] expose focus event callbacks --- .../shared/plugins/event-editor/constants.ts | 3 ++ .../event-editor/createEventEditorPlugin.ts | 13 ++++++ .../src/shared/plugins/event-editor/index.ts | 2 + .../event-editor/useFocusEditorEvents.ts | 42 +++++++++++++++++++ 4 files changed, 60 insertions(+) create mode 100644 packages/core/src/shared/plugins/event-editor/constants.ts create mode 100644 packages/core/src/shared/plugins/event-editor/useFocusEditorEvents.ts diff --git a/packages/core/src/shared/plugins/event-editor/constants.ts b/packages/core/src/shared/plugins/event-editor/constants.ts new file mode 100644 index 0000000000..f80baf5323 --- /dev/null +++ b/packages/core/src/shared/plugins/event-editor/constants.ts @@ -0,0 +1,3 @@ +export const FOCUS_EDITOR_EVENT = 'focus-editor-event'; + +export const BLUR_EDITOR_EVENT = 'blur-editor-event'; diff --git a/packages/core/src/shared/plugins/event-editor/createEventEditorPlugin.ts b/packages/core/src/shared/plugins/event-editor/createEventEditorPlugin.ts index 1f65a2564c..419879df49 100644 --- a/packages/core/src/shared/plugins/event-editor/createEventEditorPlugin.ts +++ b/packages/core/src/shared/plugins/event-editor/createEventEditorPlugin.ts @@ -1,4 +1,5 @@ import { createPluginFactory } from '../../utils/createPluginFactory'; +import { BLUR_EDITOR_EVENT, FOCUS_EDITOR_EVENT } from './constants'; import { eventEditorActions, eventEditorSelectors } from './eventEditorStore'; export const KEY_EVENT_EDITOR = 'event-editor'; @@ -13,9 +14,21 @@ export const createEventEditorPlugin = createPluginFactory({ } eventEditorActions.blur(editor.id); + + document.dispatchEvent( + new CustomEvent(BLUR_EDITOR_EVENT, { + detail: { id: editor.id }, + }) + ); }, onFocus: (editor) => () => { eventEditorActions.focus(editor.id); + + document.dispatchEvent( + new CustomEvent(FOCUS_EDITOR_EVENT, { + detail: { id: editor.id }, + }) + ); }, }, key: KEY_EVENT_EDITOR, diff --git a/packages/core/src/shared/plugins/event-editor/index.ts b/packages/core/src/shared/plugins/event-editor/index.ts index 9e47e5c787..95a9d201a1 100644 --- a/packages/core/src/shared/plugins/event-editor/index.ts +++ b/packages/core/src/shared/plugins/event-editor/index.ts @@ -5,3 +5,5 @@ export * from './createEventEditorPlugin'; export * from './eventEditorStore'; export * from './getEventPlateId'; + +export * from './useFocusEditorEvents'; diff --git a/packages/core/src/shared/plugins/event-editor/useFocusEditorEvents.ts b/packages/core/src/shared/plugins/event-editor/useFocusEditorEvents.ts new file mode 100644 index 0000000000..4af1814450 --- /dev/null +++ b/packages/core/src/shared/plugins/event-editor/useFocusEditorEvents.ts @@ -0,0 +1,42 @@ +import { useEffect } from 'react'; + +import type { Value } from '@udecode/plate-common'; + +import type { PlateEditor } from '../../types'; + +import { BLUR_EDITOR_EVENT, FOCUS_EDITOR_EVENT } from './constants'; + +export const useFocusEditorEvents = ({ + editorRef, + onEditorBlur, + onEditorFocus, +}: { + editorRef: PlateEditor | null; + onEditorBlur?: () => void; + onEditorFocus?: () => void; +}) => { + useEffect(() => { + const onFocusEditor = (event: Event) => { + const id = (event as any).detail.id; + + if (!!onEditorFocus && editorRef && editorRef.id === id) { + onEditorFocus(); + } + }; + const onBlurEditor = (event: Event) => { + const id = (event as any).detail.id; + + if (!!onEditorBlur && editorRef && editorRef.id === id) { + onEditorBlur(); + } + }; + + document.addEventListener(FOCUS_EDITOR_EVENT, onFocusEditor); + document.addEventListener(BLUR_EDITOR_EVENT, onBlurEditor); + + return () => { + document.removeEventListener(FOCUS_EDITOR_EVENT, onFocusEditor); + document.removeEventListener(BLUR_EDITOR_EVENT, onBlurEditor); + }; + }, [editorRef, onEditorBlur, onEditorFocus]); +}; From b55e8e34add056f7b7eed631536c16cf9c5a6dfd Mon Sep 17 00:00:00 2001 From: Ziad Beyens Date: Tue, 28 May 2024 15:15:31 +0200 Subject: [PATCH 32/63] Create soft-wolves-breathe.md --- .changeset/soft-wolves-breathe.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/soft-wolves-breathe.md diff --git a/.changeset/soft-wolves-breathe.md b/.changeset/soft-wolves-breathe.md new file mode 100644 index 0000000000..3cb1354fb6 --- /dev/null +++ b/.changeset/soft-wolves-breathe.md @@ -0,0 +1,5 @@ +--- +"@udecode/plate-core": patch +--- + +[event-editor] expose focus event callbacks From b0e9bd7aecba1bfffb36f5de72258ba0a86cfe56 Mon Sep 17 00:00:00 2001 From: Dzmitry Tamashevich Date: Thu, 30 May 2024 14:34:35 +0300 Subject: [PATCH 33/63] change Value import --- .../src/shared/plugins/event-editor/useFocusEditorEvents.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/shared/plugins/event-editor/useFocusEditorEvents.ts b/packages/core/src/shared/plugins/event-editor/useFocusEditorEvents.ts index 4af1814450..1a739deb52 100644 --- a/packages/core/src/shared/plugins/event-editor/useFocusEditorEvents.ts +++ b/packages/core/src/shared/plugins/event-editor/useFocusEditorEvents.ts @@ -1,6 +1,6 @@ import { useEffect } from 'react'; -import type { Value } from '@udecode/plate-common'; +import type { Value } from '@udecode/slate'; import type { PlateEditor } from '../../types'; From 13e7f44f5bf7e112c941a049d29f81a89977f976 Mon Sep 17 00:00:00 2001 From: Dzmitry Tamashevich Date: Thu, 30 May 2024 14:56:52 +0300 Subject: [PATCH 34/63] fix test --- .../serializer-md/src/deserializer/utils/deserializeMd.spec.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/serializer-md/src/deserializer/utils/deserializeMd.spec.tsx b/packages/serializer-md/src/deserializer/utils/deserializeMd.spec.tsx index f8496a3be9..762cded4b4 100644 --- a/packages/serializer-md/src/deserializer/utils/deserializeMd.spec.tsx +++ b/packages/serializer-md/src/deserializer/utils/deserializeMd.spec.tsx @@ -294,7 +294,7 @@ describe('deserializeMd', () => { Line 1 - {'\n'} + {'
'}
Line 2
From 314138a001269d58b993262dac98f480dc964589 Mon Sep 17 00:00:00 2001 From: Dzmitry Tamashevich Date: Thu, 30 May 2024 15:58:46 +0300 Subject: [PATCH 35/63] revert unrelated test fix --- .../serializer-md/src/deserializer/utils/deserializeMd.spec.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/serializer-md/src/deserializer/utils/deserializeMd.spec.tsx b/packages/serializer-md/src/deserializer/utils/deserializeMd.spec.tsx index 762cded4b4..f8496a3be9 100644 --- a/packages/serializer-md/src/deserializer/utils/deserializeMd.spec.tsx +++ b/packages/serializer-md/src/deserializer/utils/deserializeMd.spec.tsx @@ -294,7 +294,7 @@ describe('deserializeMd', () => { Line 1 - {'
'}
+ {'\n'} Line 2
From 8cf1b3d87c0396fbb60e678a9087c04ea62d19bf Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Mon, 3 Jun 2024 17:10:49 +0100 Subject: [PATCH 36/63] Use combobox with emoji picker --- apps/www/public/registry/index.json | 13 +- apps/www/src/__registry__/index.tsx | 7 - apps/www/src/config/customizer-components.ts | 4 - apps/www/src/config/customizer-items.ts | 14 +- apps/www/src/config/docs.ts | 1 - apps/www/src/lib/plate/create-plate-ui.ts | 4 + .../src/lib/plate/demo/plugins/emojiPlugin.ts | 8 -- .../src/lib/plate/demo/values/emojiValue.tsx | 2 +- .../default/example/playground-demo.tsx | 3 +- .../default/plate-ui/emoji-combobox.tsx | 43 ------ .../default/plate-ui/emoji-input-element.tsx | 69 ++++++++++ .../default/plate-ui/inline-combobox.tsx | 96 +++++++------ apps/www/src/registry/registry.ts | 7 - packages/combobox/src/utils/filterWords.ts | 6 +- packages/emoji/src/constants.ts | 15 +- packages/emoji/src/createEmojiPlugin.ts | 36 ++--- .../emoji/src/handlers/getEmojiOnInsert.ts | 30 ---- .../src/handlers/getEmojiOnSelectItem.ts | 42 ------ .../src/handlers/getFindTriggeringInput.ts | 80 ----------- packages/emoji/src/handlers/index.ts | 5 - packages/emoji/src/hooks/index.ts | 1 - packages/emoji/src/hooks/useEmojiCombobox.ts | 29 ---- packages/emoji/src/index.ts | 2 - packages/emoji/src/types.ts | 30 +--- .../utils/EmojiLibrary/EmojiLibrary.types.ts | 23 +--- .../src/utils/EmojiPicker/useEmojiPicker.ts | 17 +-- .../src/utils/EmojiTriggeringController.ts | 82 ----------- .../IndexSearch/EmojiFloatingIndexSearch.ts | 8 +- .../IndexSearch/EmojiInlineIndexSearch.ts | 21 +-- .../src/utils/IndexSearch/IndexSearch.ts | 21 +-- packages/emoji/src/utils/index.ts | 2 +- packages/emoji/src/utils/insertEmoji.ts | 30 ++++ packages/emoji/src/withEmoji.ts | 130 ------------------ .../slash-command/src/createSlashPlugin.ts | 1 - 34 files changed, 217 insertions(+), 665 deletions(-) delete mode 100644 apps/www/src/lib/plate/demo/plugins/emojiPlugin.ts delete mode 100644 apps/www/src/registry/default/plate-ui/emoji-combobox.tsx create mode 100644 apps/www/src/registry/default/plate-ui/emoji-input-element.tsx delete mode 100644 packages/emoji/src/handlers/getEmojiOnInsert.ts delete mode 100644 packages/emoji/src/handlers/getEmojiOnSelectItem.ts delete mode 100644 packages/emoji/src/handlers/getFindTriggeringInput.ts delete mode 100644 packages/emoji/src/handlers/index.ts delete mode 100644 packages/emoji/src/hooks/useEmojiCombobox.ts delete mode 100644 packages/emoji/src/utils/EmojiTriggeringController.ts create mode 100644 packages/emoji/src/utils/insertEmoji.ts delete mode 100644 packages/emoji/src/withEmoji.ts diff --git a/apps/www/public/registry/index.json b/apps/www/public/registry/index.json index a537fc72ef..83a569a60d 100644 --- a/apps/www/public/registry/index.json +++ b/apps/www/public/registry/index.json @@ -311,17 +311,6 @@ ], "type": "components:plate-ui" }, - { - "name": "emoji-combobox", - "dependencies": [ - "@udecode/plate-combobox" - ], - "registryDependencies": [], - "files": [ - "plate-ui/emoji-combobox.tsx" - ], - "type": "components:plate-ui" - }, { "name": "excalidraw-element", "dependencies": [ @@ -894,4 +883,4 @@ ], "type": "components:plate-ui" } -] \ No newline at end of file +] diff --git a/apps/www/src/__registry__/index.tsx b/apps/www/src/__registry__/index.tsx index ec10a10a2b..08c957b105 100644 --- a/apps/www/src/__registry__/index.tsx +++ b/apps/www/src/__registry__/index.tsx @@ -368,13 +368,6 @@ export const Index: Record = { files: ['registry/default/plate-ui/dropdown-menu.tsx'], component: React.lazy(() => import('@/registry/default/plate-ui/dropdown-menu')), }, - 'emoji-combobox': { - name: 'emoji-combobox', - type: 'components:plate-ui', - registryDependencies: [], - files: ['registry/default/plate-ui/emoji-combobox.tsx'], - component: React.lazy(() => import('@/registry/default/plate-ui/emoji-combobox')), - }, 'excalidraw-element': { name: 'excalidraw-element', type: 'components:plate-ui', diff --git a/apps/www/src/config/customizer-components.ts b/apps/www/src/config/customizer-components.ts index 71312cbb37..c02290e736 100644 --- a/apps/www/src/config/customizer-components.ts +++ b/apps/www/src/config/customizer-components.ts @@ -76,10 +76,6 @@ export const customizerComponents = { href: '/docs/components/editor', title: 'Editor', }, - emojiCombobox: { - href: '/docs/components/emoji-combobox', - title: 'Emoji Combobox', - }, emojiDropdownMenu: { href: '/docs/components/emoji-dropdown-menu', title: 'Emoji Dropdown Menu', diff --git a/apps/www/src/config/customizer-items.ts b/apps/www/src/config/customizer-items.ts index 830f53f0c3..222042f0a1 100644 --- a/apps/www/src/config/customizer-items.ts +++ b/apps/www/src/config/customizer-items.ts @@ -500,13 +500,13 @@ export const customizerItems: Record = { [KEY_EMOJI]: { badges: [customizerBadges.handler], components: [ - { - id: 'emoji-combobox', - label: 'EmojiCombobox', - pluginOptions: [`renderAfterEditable: EmojiCombobox,`], - route: customizerComponents.emojiCombobox.href, - usage: 'EmojiCombobox', - }, + // { + // id: 'emoji-combobox', + // label: 'EmojiCombobox', + // pluginOptions: [`renderAfterEditable: EmojiCombobox,`], + // route: customizerComponents.emojiCombobox.href, + // usage: 'EmojiCombobox', + // }, ], dependencies: [KEY_COMBOBOX], id: KEY_EMOJI, diff --git a/apps/www/src/config/docs.ts b/apps/www/src/config/docs.ts index dff5e2ed5f..0345fa92ee 100644 --- a/apps/www/src/config/docs.ts +++ b/apps/www/src/config/docs.ts @@ -69,7 +69,6 @@ export const docsConfig: DocsConfig = { customizerComponents.dialog, customizerComponents.draggable, customizerComponents.dropdownMenu, - customizerComponents.emojiCombobox, customizerComponents.emojiDropdownMenu, customizerComponents.emojiToolbarDropdown, customizerComponents.excalidrawElement, diff --git a/apps/www/src/lib/plate/create-plate-ui.ts b/apps/www/src/lib/plate/create-plate-ui.ts index ccf3195330..a4f4de4210 100644 --- a/apps/www/src/lib/plate/create-plate-ui.ts +++ b/apps/www/src/lib/plate/create-plate-ui.ts @@ -61,6 +61,7 @@ import { CodeSyntaxLeaf } from '@/registry/default/plate-ui/code-syntax-leaf'; import { ColumnElement } from '@/registry/default/plate-ui/column-element'; import { ColumnGroupElement } from '@/registry/default/plate-ui/column-group-element'; import { CommentLeaf } from '@/registry/default/plate-ui/comment-leaf'; +import { EmojiInputElement } from '@/registry/default/plate-ui/emoji-input-element'; import { ExcalidrawElement } from '@/registry/default/plate-ui/excalidraw-element'; import { HeadingElement } from '@/registry/default/plate-ui/heading-element'; import { HighlightLeaf } from '@/registry/default/plate-ui/highlight-leaf'; @@ -86,6 +87,8 @@ import { TodoListElement } from '@/registry/default/plate-ui/todo-list-element'; import { ToggleElement } from '@/registry/default/plate-ui/toggle-element'; import { withDraggables } from '@/registry/default/plate-ui/with-draggables'; +import { ELEMENT_EMOJI_INPUT } from '../../../../../packages/emoji/dist'; + export const createPlateUI = ( overrideByKey?: Partial>, { @@ -100,6 +103,7 @@ export const createPlateUI = ( [ELEMENT_CODE_SYNTAX]: CodeSyntaxLeaf, [ELEMENT_COLUMN]: ColumnElement, [ELEMENT_COLUMN_GROUP]: ColumnGroupElement, + [ELEMENT_EMOJI_INPUT]: EmojiInputElement, [ELEMENT_EXCALIDRAW]: ExcalidrawElement, [ELEMENT_H1]: withProps(HeadingElement, { variant: 'h1' }), [ELEMENT_H2]: withProps(HeadingElement, { variant: 'h2' }), diff --git a/apps/www/src/lib/plate/demo/plugins/emojiPlugin.ts b/apps/www/src/lib/plate/demo/plugins/emojiPlugin.ts deleted file mode 100644 index e4b2a00142..0000000000 --- a/apps/www/src/lib/plate/demo/plugins/emojiPlugin.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { PlatePlugin } from '@udecode/plate-common'; -import type { EmojiPlugin } from '@udecode/plate-emoji'; - -import { EmojiCombobox } from '@/registry/default/plate-ui/emoji-combobox'; - -export const emojiPlugin: Partial> = { - renderAfterEditable: EmojiCombobox, -}; diff --git a/apps/www/src/lib/plate/demo/values/emojiValue.tsx b/apps/www/src/lib/plate/demo/values/emojiValue.tsx index 3de8eb391a..91e1e43e5a 100644 --- a/apps/www/src/lib/plate/demo/values/emojiValue.tsx +++ b/apps/www/src/lib/plate/demo/values/emojiValue.tsx @@ -9,7 +9,7 @@ export const emojiValue: any = ( 🙂 Emoji's Express yourself with a touch of fun 🎉 and emotion 😃. - Pick from the toolbar or write after the colon to open the combobox : + Pick from the toolbar or type a colon to open the combobox. ); diff --git a/apps/www/src/registry/default/example/playground-demo.tsx b/apps/www/src/registry/default/example/playground-demo.tsx index 5dfceeda00..c39bcbe738 100644 --- a/apps/www/src/registry/default/example/playground-demo.tsx +++ b/apps/www/src/registry/default/example/playground-demo.tsx @@ -105,7 +105,6 @@ import { autoformatIndentLists } from '@/plate/demo/plugins/autoformatIndentList import { autoformatLists } from '@/plate/demo/plugins/autoformatLists'; import { autoformatRules } from '@/plate/demo/plugins/autoformatRules'; import { dragOverCursorPlugin } from '@/plate/demo/plugins/dragOverCursorPlugin'; -import { emojiPlugin } from '@/plate/demo/plugins/emojiPlugin'; import { exitBreakPlugin } from '@/plate/demo/plugins/exitBreakPlugin'; import { forcedLayoutPlugin } from '@/plate/demo/plugins/forcedLayoutPlugin'; import { lineHeightPlugin } from '@/plate/demo/plugins/lineHeightPlugin'; @@ -282,7 +281,7 @@ export const usePlaygroundPlugins = ({ enabled: !!enabled.dnd, options: { enableScroller: true }, }), - createEmojiPlugin({ ...emojiPlugin, enabled: !!enabled.emoji }), + createEmojiPlugin({ enabled: !!enabled.emoji }), createExitBreakPlugin({ ...exitBreakPlugin, enabled: !!enabled.exitBreak, diff --git a/apps/www/src/registry/default/plate-ui/emoji-combobox.tsx b/apps/www/src/registry/default/plate-ui/emoji-combobox.tsx deleted file mode 100644 index b8c0ed0b88..0000000000 --- a/apps/www/src/registry/default/plate-ui/emoji-combobox.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import React from 'react'; - -import type { ComboboxItemProps } from '@udecode/plate-combobox'; - -import { - type EmojiItemData, - KEY_EMOJI, - type TEmojiCombobox, - useEmojiComboboxState, -} from '@udecode/plate-emoji'; - -import { Combobox } from './combobox'; - -export function EmojiComboboxItem({ item }: ComboboxItemProps) { - const { - data: { emoji, id }, - } = item; - - return ( -
- {emoji} :{id}: -
- ); -} - -export function EmojiCombobox({ - pluginKey = KEY_EMOJI, - id = pluginKey, - ...props -}: TEmojiCombobox) { - const { onSelectItem, trigger } = useEmojiComboboxState({ pluginKey }); - - return ( - - ); -} diff --git a/apps/www/src/registry/default/plate-ui/emoji-input-element.tsx b/apps/www/src/registry/default/plate-ui/emoji-input-element.tsx new file mode 100644 index 0000000000..4192b3eb8c --- /dev/null +++ b/apps/www/src/registry/default/plate-ui/emoji-input-element.tsx @@ -0,0 +1,69 @@ +import React, { useMemo, useState } from 'react'; + +import { withRef } from '@udecode/cn'; +import { PlateElement } from '@udecode/plate-common'; +import { EmojiInlineIndexSearch, insertEmoji } from '@udecode/plate-emoji'; + +import { useDebounce } from '@/hooks/use-debounce'; + +import { + InlineCombobox, + InlineComboboxContent, + InlineComboboxEmpty, + InlineComboboxInput, + InlineComboboxItem, +} from './inline-combobox'; + +export const EmojiInputElement = withRef( + ({ className, ...props }, ref) => { + const { children, editor, element } = props; + const [value, setValue] = useState(''); + const debouncedValue = useDebounce(value, 100); + const isPending = value !== debouncedValue; + + const filteredEmojis = useMemo(() => { + if (debouncedValue.trim().length === 0) return []; + + return EmojiInlineIndexSearch.getInstance() + .search(debouncedValue.replace(/:$/, '')) + .get(); + }, [debouncedValue]); + + return ( + + + + + + {!isPending && ( + No matching emoji found + )} + + {filteredEmojis.map((emoji) => ( + insertEmoji(editor, emoji)} + value={emoji.name} + > + {emoji.skins[0].native} {emoji.name} + + ))} + + + + {children} + + ); + } +); diff --git a/apps/www/src/registry/default/plate-ui/inline-combobox.tsx b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx index 0cdc101928..4e6f202520 100644 --- a/apps/www/src/registry/default/plate-ui/inline-combobox.tsx +++ b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx @@ -5,11 +5,10 @@ import React, { createContext, forwardRef, startTransition, + useCallback, useContext, useEffect, useMemo, - useReducer, - useRef, useState, } from 'react'; @@ -20,6 +19,8 @@ import { ComboboxPopover, ComboboxProvider, Portal, + useComboboxContext, + useComboboxStore, } from '@ariakit/react'; import { cn } from '@udecode/cn'; import { @@ -42,7 +43,6 @@ type FilterFn = ( ) => boolean; interface InlineComboboxContextValue { - dispatchVisible: (action: 'decrement' | 'increment') => void; filter: FilterFn | false; inputProps: UseComboboxInputResult['props']; inputRef: RefObject; @@ -50,21 +50,20 @@ interface InlineComboboxContextValue { setHasEmpty: (hasEmpty: boolean) => void; showTrigger: boolean; trigger: string; - value: string; - visibleCount: number; } const InlineComboboxContext = createContext( null as any ); -const defaultFilter: FilterFn = ({ keywords = [], value }, search) => +export const defaultFilter: FilterFn = ({ keywords = [], value }, search) => [value, ...keywords].some((keyword) => filterWords(keyword, search)); interface InlineComboboxProps { children: ReactNode; trigger: string; filter?: FilterFn | false; + hideWhenNoValue?: boolean; setValue?: (value: string) => void; showTrigger?: boolean; value?: string; @@ -73,6 +72,7 @@ interface InlineComboboxProps { const InlineCombobox = ({ children, filter = defaultFilter, + hideWhenNoValue = false, setValue: setValueProp, showTrigger = true, trigger, @@ -85,7 +85,17 @@ const InlineCombobox = ({ const [valueState, setValueState] = useState(''); const hasValueProp = valueProp !== undefined; const value = hasValueProp ? valueProp : valueState; - const setValue = hasValueProp ? setValueProp ?? (() => {}) : setValueState; + + const setValue = useCallback( + (newValue: string) => { + setValueProp?.(newValue); + + if (!hasValueProp) { + setValueState(newValue); + } + }, + [setValueProp, hasValueProp] + ); const { props: inputProps, removeInput } = useComboboxInput({ cancelInputOnBlur: false, @@ -104,17 +114,10 @@ const InlineCombobox = ({ ref: inputRef, }); - const [visibleCount, dispatchVisible] = useReducer( - (state: number, action: 'decrement' | 'increment') => - state + (action === 'increment' ? 1 : -1), - 0 - ); - const [hasEmpty, setHasEmpty] = useState(false); const contextValue: InlineComboboxContextValue = useMemo( () => ({ - dispatchVisible, filter, inputProps, inputRef, @@ -122,27 +125,46 @@ const InlineCombobox = ({ setHasEmpty, showTrigger, trigger, - value, - visibleCount, }), [ trigger, showTrigger, filter, - value, inputRef, inputProps, removeInput, - visibleCount, setHasEmpty, ] ); + const store = useComboboxStore({ + // open: , + setValue: (newValue) => startTransition(() => setValue(newValue)), + }); + + const items = store.useState('items'); + + useEffect; + + /** + * If there is no active ID and the list of items changes, select the first + * item. + */ + useEffect(() => { + if (!store.getState().activeId) { + store.setActiveId(store.first()); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [items, store]); + return ( 0 || hasEmpty} - setValue={(newValue) => startTransition(() => setValue(newValue))} + open={ + (items.length > 0 || hasEmpty) && + (!hideWhenNoValue || value.length > 0) + } + store={store} > {children} @@ -161,9 +183,11 @@ const InlineComboboxInput = forwardRef< inputRef: contextRef, showTrigger, trigger, - value, } = useContext(InlineComboboxContext); + const store = useComboboxContext()!; + const value = store.useState('value'); + const ref = useComposedRef(propRef, contextRef); /** @@ -249,28 +273,18 @@ const InlineComboboxItem = ({ }: InlineComboboxItemProps) => { const { value } = props; - const { - dispatchVisible, - filter, - removeInput, - value: search, - } = useContext(InlineComboboxContext); + const { filter, removeInput } = useContext(InlineComboboxContext); + + const store = useComboboxContext()!; + + // Optimization: Do not subscribe to value if filter is false + const search = filter && store.useState('value'); const visible = useMemo( - () => !filter || filter({ keywords, value }, search), + () => !filter || filter({ keywords, value }, search as string), [filter, value, keywords, search] ); - const previousVisibleRef = useRef(false); - - useEffect(() => { - if (visible !== previousVisibleRef.current) { - dispatchVisible(visible ? 'increment' : 'decrement'); - } - - previousVisibleRef.current = visible; - }, [dispatchVisible, visible]); - if (!visible) return null; return ( @@ -289,7 +303,9 @@ const InlineComboboxEmpty = ({ children, className, }: HTMLAttributes) => { - const { setHasEmpty, visibleCount } = useContext(InlineComboboxContext); + const { setHasEmpty } = useContext(InlineComboboxContext); + const store = useComboboxContext()!; + const items = store.useState('items'); useEffect(() => { setHasEmpty(true); @@ -299,7 +315,7 @@ const InlineComboboxEmpty = ({ }; }, [setHasEmpty]); - if (visibleCount > 0) return null; + if (items.length > 0) return null; return (
{ - const haystackWords = haystack.trim().split(/\s+/); - const needleWords = needle.trim().split(/\s+/); + const haystackWords = haystack.trim().split(wordBoundary); + const needleWords = needle.trim().split(wordBoundary); const quantifier = wordQuantifier === 'match-all' ? 'every' : 'some'; diff --git a/packages/emoji/src/constants.ts b/packages/emoji/src/constants.ts index 09cfcb00fc..4e29780a05 100644 --- a/packages/emoji/src/constants.ts +++ b/packages/emoji/src/constants.ts @@ -1,7 +1,4 @@ -import type { - EmojiTriggeringControllerOptions, - FrequentEmojis, -} from './utils/index'; +import type { FrequentEmojis } from './utils/index'; import { EmojiCategory, @@ -10,18 +7,8 @@ import { type i18nProps, } from './types'; -export const KEY_EMOJI = 'emoji'; - -export const EMOJI_TRIGGER = ':'; - export const EMOJI_MAX_SEARCH_RESULT = 60; -export const emojiTriggeringControllerOptions: EmojiTriggeringControllerOptions = - { - limitTriggeringChars: 2, - trigger: EMOJI_TRIGGER, - }; - export const defaultCategories: EmojiCategoryList[] = [ EmojiCategory.People, EmojiCategory.Nature, diff --git a/packages/emoji/src/createEmojiPlugin.ts b/packages/emoji/src/createEmojiPlugin.ts index 38e6c34f6f..5e267346ed 100644 --- a/packages/emoji/src/createEmojiPlugin.ts +++ b/packages/emoji/src/createEmojiPlugin.ts @@ -1,28 +1,30 @@ +import { withTriggerCombobox } from '@udecode/plate-combobox'; import { createPluginFactory } from '@udecode/plate-common/server'; import type { EmojiPlugin } from './types'; -import { EMOJI_TRIGGER, KEY_EMOJI } from './constants'; -import { EmojiTriggeringController } from './utils/index'; -import { withEmoji } from './withEmoji'; +export const KEY_EMOJI = 'emoji'; + +export const ELEMENT_EMOJI_INPUT = 'emoji_input'; export const createEmojiPlugin = createPluginFactory({ key: KEY_EMOJI, options: { - createEmoji: (item) => item.data.emoji, - emojiTriggeringController: new EmojiTriggeringController(), - trigger: EMOJI_TRIGGER, + createComboboxInput: () => ({ + children: [{ text: '' }], + type: ELEMENT_EMOJI_INPUT, + }), + createEmojiNode: ({ skins }) => ({ text: skins[0].native }), + trigger: ':', + triggerPreviousCharPattern: /^\s?$/, }, - then: ( - _, - { key, options: { createEmoji, emojiTriggeringController, trigger } } - ) => ({ - options: { - createEmoji, - emojiTriggeringController, - id: key, - trigger, + plugins: [ + { + isElement: true, + isInline: true, + isVoid: true, + key: ELEMENT_EMOJI_INPUT, }, - }), - withOverrides: withEmoji, + ], + withOverrides: withTriggerCombobox, }); diff --git a/packages/emoji/src/handlers/getEmojiOnInsert.ts b/packages/emoji/src/handlers/getEmojiOnInsert.ts deleted file mode 100644 index d7a5a28fa8..0000000000 --- a/packages/emoji/src/handlers/getEmojiOnInsert.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { ComboboxOnSelectItem } from '@udecode/plate-combobox'; - -import { focusEditor } from '@udecode/plate-common'; -import { - type PlatePluginKey, - getPlugin, - insertText, - withoutNormalizing, -} from '@udecode/plate-common/server'; - -import type { EmojiItemData, EmojiPlugin } from '../types'; - -import { KEY_EMOJI } from '../constants'; - -export const getEmojiOnInsert = - ({ - key = KEY_EMOJI, - }: PlatePluginKey = {}): ComboboxOnSelectItem => - (editor, item) => { - const { - options: { createEmoji }, - } = getPlugin(editor as any, key); - - withoutNormalizing(editor, () => { - focusEditor(editor); - - const value = createEmoji!(item); - insertText(editor, value); - }); - }; diff --git a/packages/emoji/src/handlers/getEmojiOnSelectItem.ts b/packages/emoji/src/handlers/getEmojiOnSelectItem.ts deleted file mode 100644 index 020c371d37..0000000000 --- a/packages/emoji/src/handlers/getEmojiOnSelectItem.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { - type ComboboxOnSelectItem, - comboboxActions, -} from '@udecode/plate-combobox'; -import { - type PlatePluginKey, - deleteText, - getPlugin, - insertText, - withoutMergingHistory, - withoutNormalizing, -} from '@udecode/plate-common/server'; - -import type { EmojiItemData, EmojiPlugin } from '../types'; - -import { KEY_EMOJI } from '../constants'; - -export const getEmojiOnSelectItem = - ({ - key = KEY_EMOJI, - }: PlatePluginKey = {}): ComboboxOnSelectItem => - (editor, item) => { - const { - options: { createEmoji, emojiTriggeringController }, - } = getPlugin(editor as any, key); - - withoutNormalizing(editor, () => { - withoutMergingHistory(editor, () => - deleteText(editor, { - distance: emojiTriggeringController! - .setIsTriggering(false) - .getTextSize(), - reverse: true, - }) - ); - - const value = createEmoji!(item); - insertText(editor, value); - }); - - return comboboxActions.reset(); - }; diff --git a/packages/emoji/src/handlers/getFindTriggeringInput.ts b/packages/emoji/src/handlers/getFindTriggeringInput.ts deleted file mode 100644 index 83d2352210..0000000000 --- a/packages/emoji/src/handlers/getFindTriggeringInput.ts +++ /dev/null @@ -1,80 +0,0 @@ -import type { BasePoint } from 'slate'; - -import { - type PlateEditor, - type Value, - getEditorString, - getPointBefore, - getRange, - isCollapsed, -} from '@udecode/plate-common/server'; - -import type { FindTriggeringInputProps } from '../types'; -import type { IEmojiTriggeringController } from '../utils/index'; - -const isSpaceBreak = (char?: string) => !!char && /\s/.test(char); - -const getPreviousChar = ( - editor: PlateEditor, - point?: BasePoint -) => - point - ? getEditorString( - editor, - getRange(editor, point, getPointBefore(editor, point)) - ) - : undefined; - -const getPreviousPoint = ( - editor: PlateEditor, - point?: BasePoint -) => (point ? getPointBefore(editor, point) : undefined); - -const isBeginningOfTheLine = ( - editor: PlateEditor, - point?: BasePoint -) => { - const previousPoint = getPreviousPoint(editor, point); - - return point?.path[0] !== previousPoint?.path[0]; -}; - -export const getFindTriggeringInput = - ( - editor: PlateEditor, - emojiTriggeringController: IEmojiTriggeringController - ) => - ({ action = 'insert', char = '' }: FindTriggeringInputProps = {}) => { - const { selection } = editor; - - if (!selection || !isCollapsed(selection) || isSpaceBreak(char)) { - emojiTriggeringController.setIsTriggering(false); - - return; - } - - const startPoint = selection.anchor; - let currentPoint: BasePoint | undefined = startPoint; - let previousPoint; - - let foundText = char; - let previousChar; - - do { - previousChar = getPreviousChar(editor, currentPoint); - foundText = previousChar + foundText; - previousPoint = getPreviousPoint(editor, currentPoint); - - if (isBeginningOfTheLine(editor, currentPoint)) { - break; - } - - currentPoint = previousPoint; - } while (!isSpaceBreak(previousChar)); - - foundText = foundText.trim(); - - if (action === 'delete') foundText = foundText.slice(0, -1); - - emojiTriggeringController.setText(foundText); - }; diff --git a/packages/emoji/src/handlers/index.ts b/packages/emoji/src/handlers/index.ts deleted file mode 100644 index d64aefba50..0000000000 --- a/packages/emoji/src/handlers/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** @file Automatically generated by barrelsby. */ - -export * from './getEmojiOnInsert'; -export * from './getEmojiOnSelectItem'; -export * from './getFindTriggeringInput'; diff --git a/packages/emoji/src/hooks/index.ts b/packages/emoji/src/hooks/index.ts index ea2820c600..5aa526af1f 100644 --- a/packages/emoji/src/hooks/index.ts +++ b/packages/emoji/src/hooks/index.ts @@ -1,4 +1,3 @@ /** @file Automatically generated by barrelsby. */ -export * from './useEmojiCombobox'; export * from './useEmojiDropdownMenuState'; diff --git a/packages/emoji/src/hooks/useEmojiCombobox.ts b/packages/emoji/src/hooks/useEmojiCombobox.ts deleted file mode 100644 index dd44f61d80..0000000000 --- a/packages/emoji/src/hooks/useEmojiCombobox.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { - ComboboxOnSelectItem, - ComboboxProps, - Data, - NoData, -} from '@udecode/plate-combobox'; - -import { useEditorRef } from '@udecode/plate-common'; -import { getPluginOptions } from '@udecode/plate-common/server'; - -import { type EmojiPlugin, getEmojiOnSelectItem } from '../index'; - -export interface TEmojiCombobox - extends Partial> { - onSelectItem?: ComboboxOnSelectItem | null; - pluginKey?: string; -} - -export const useEmojiComboboxState = ({ pluginKey }: { pluginKey: string }) => { - const editor = useEditorRef(); - const { trigger } = getPluginOptions(editor, pluginKey); - - const onSelectItem = getEmojiOnSelectItem({ key: pluginKey }); - - return { - onSelectItem, - trigger: trigger!, - }; -}; diff --git a/packages/emoji/src/index.ts b/packages/emoji/src/index.ts index b93476a796..ac780c2573 100644 --- a/packages/emoji/src/index.ts +++ b/packages/emoji/src/index.ts @@ -3,8 +3,6 @@ export * from './constants'; export * from './createEmojiPlugin'; export * from './types'; -export * from './withEmoji'; -export * from './handlers/index'; export * from './hooks/index'; export * from './storage/index'; export * from './utils/index'; diff --git a/packages/emoji/src/types.ts b/packages/emoji/src/types.ts index 3ff72de5a4..c35e87f9d3 100644 --- a/packages/emoji/src/types.ts +++ b/packages/emoji/src/types.ts @@ -1,6 +1,6 @@ -import type { TComboboxItem } from '@udecode/plate-combobox'; - -import type { IEmojiTriggeringController } from './utils/index'; +import type { Emoji } from '@emoji-mart/data'; +import type { TriggerComboboxPlugin } from '@udecode/plate-combobox'; +import type { EElementOrText } from '@udecode/plate-common'; type ReverseMap = T[keyof T]; @@ -22,22 +22,9 @@ export type EmojiSettingsType = { }; }; -export type EmojiItemData = { - emoji: string; - id: string; - name: string; - text: string; -}; - -export type CreateEmoji = ( - data: TComboboxItem -) => string; - -export interface EmojiPlugin { - createEmoji?: CreateEmoji; - emojiTriggeringController?: IEmojiTriggeringController; - id?: string; - trigger?: string; +export interface EmojiPlugin + extends TriggerComboboxPlugin { + createEmojiNode?: (emoji: TEmoji) => EElementOrText; } export const EmojiCategory = { @@ -73,8 +60,3 @@ export type EmojiIconList = { loupe: T; }; }; - -export type FindTriggeringInputProps = { - action?: 'delete' | 'insert'; - char?: string; -}; diff --git a/packages/emoji/src/utils/EmojiLibrary/EmojiLibrary.types.ts b/packages/emoji/src/utils/EmojiLibrary/EmojiLibrary.types.ts index a7f43fc59d..975a0a98cd 100644 --- a/packages/emoji/src/utils/EmojiLibrary/EmojiLibrary.types.ts +++ b/packages/emoji/src/utils/EmojiLibrary/EmojiLibrary.types.ts @@ -1,32 +1,17 @@ +import type { Emoji, EmojiMartData } from '@emoji-mart/data'; /** * Emoji: type Emoji = { id: string; name: string; keywords: string[]; skins: [ * { unified: '1f389'; native: '🎉'; shortcodes: ':tada:'; } ]; version: 1; }; */ -type Skin = { - native: string; - unified: string; -}; - -export type Emoji = { - id: string; - keywords: string[]; - name: string; - skins: Skin[]; - version: number; -}; - export type Emojis = Record; -export type EmojiLibrary = { - aliases: any; - categories: any[]; - emojis: Emojis; - sheet: any; -}; +export type EmojiLibrary = EmojiMartData; export interface IEmojiLibrary { getEmoji: (key: string) => Emoji; getEmojiId: (key: string) => string; keys: string[]; } + +export { type Emoji } from '@emoji-mart/data'; diff --git a/packages/emoji/src/utils/EmojiPicker/useEmojiPicker.ts b/packages/emoji/src/utils/EmojiPicker/useEmojiPicker.ts index 6fe74e9484..f3b372147e 100644 --- a/packages/emoji/src/utils/EmojiPicker/useEmojiPicker.ts +++ b/packages/emoji/src/utils/EmojiPicker/useEmojiPicker.ts @@ -12,11 +12,11 @@ import type { Emoji, IEmojiFloatingLibrary } from '../EmojiLibrary/index'; import type { AIndexSearch } from '../IndexSearch/index'; import { i18n } from '../../constants'; -import { getEmojiOnInsert } from '../../handlers/getEmojiOnInsert'; import { type SetFocusedAndVisibleSectionsType, observeCategories, } from '../EmojiObserver'; +import { insertEmoji } from '../insertEmoji'; import { EmojiPickerState, type MapEmojiCategoryList, @@ -30,7 +30,7 @@ export type MutableRefs = React.MutableRefObject<{ export type UseEmojiPickerProps = { closeOnSelect: boolean; emojiLibrary: IEmojiFloatingLibrary; - indexSearch: AIndexSearch; + indexSearch: AIndexSearch; }; export type UseEmojiPickerType< @@ -153,18 +153,7 @@ export const useEmojiPicker = ({ const onSelectEmoji = React.useCallback( (emoji: Emoji) => { - const selectItem = getEmojiOnInsert(); - selectItem(editor, { - data: { - emoji: emoji.skins[0].native, - id: emoji.id, - name: emoji.name, - text: emoji.name, - }, - key: emoji.id, - text: emoji.name, - }); - + insertEmoji(editor, emoji); updateFrequentEmojis(emoji.id); }, [editor, updateFrequentEmojis] diff --git a/packages/emoji/src/utils/EmojiTriggeringController.ts b/packages/emoji/src/utils/EmojiTriggeringController.ts deleted file mode 100644 index eb464a619b..0000000000 --- a/packages/emoji/src/utils/EmojiTriggeringController.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { emojiTriggeringControllerOptions } from '../index'; - -export type EmojiTriggeringControllerOptions = { - limitTriggeringChars: number; - trigger: string; -}; - -export interface IEmojiTriggeringController { - getText: () => string; - getTextSize: () => number; - hasEnclosingTriggeringMark: () => boolean; - hasTriggeringMark: boolean; - isTriggering: boolean; - reset: () => this; - setIsTriggering: (isTriggering: boolean) => this; - setText: (text: string) => this; -} - -export class EmojiTriggeringController implements IEmojiTriggeringController { - private _hasTriggeringMark = false; - private _isTriggering = false; - protected pos: any; - protected text = ''; - - constructor( - protected options: EmojiTriggeringControllerOptions = emojiTriggeringControllerOptions - ) {} - - private endsWithEnclosingMark(text: string) { - return new RegExp(`${this.options.trigger}$`).test(text); - } - - private startsWithTriggeringMark(text: string) { - return new RegExp(`^${this.options.trigger}`).test(text); - } - - getText() { - return this.text.replaceAll(/^:|:$/g, ''); - } - - getTextSize() { - return this.text.length; - } - - hasEnclosingTriggeringMark(): boolean { - return this.endsWithEnclosingMark(this.text); - } - - reset() { - this.text = ''; - this.setIsTriggering(false); - this._hasTriggeringMark = false; - - return this; - } - - setIsTriggering(isTriggering: boolean) { - this._isTriggering = isTriggering; - - return this; - } - - setText(text: string) { - this._hasTriggeringMark = this.startsWithTriggeringMark(text); - - this.setIsTriggering( - this._hasTriggeringMark && text.length > this.options.limitTriggeringChars - ); - - this.text = this.isTriggering ? text : ''; - - return this; - } - - get hasTriggeringMark(): boolean { - return this._hasTriggeringMark; - } - - get isTriggering(): boolean { - return this._isTriggering; - } -} diff --git a/packages/emoji/src/utils/IndexSearch/EmojiFloatingIndexSearch.ts b/packages/emoji/src/utils/IndexSearch/EmojiFloatingIndexSearch.ts index 12fdad0e60..3e17577b64 100644 --- a/packages/emoji/src/utils/IndexSearch/EmojiFloatingIndexSearch.ts +++ b/packages/emoji/src/utils/IndexSearch/EmojiFloatingIndexSearch.ts @@ -1,8 +1,8 @@ -import type { Emoji, IEmojiLibrary } from '../EmojiLibrary/index'; +import type { IEmojiLibrary } from '../EmojiLibrary/index'; import { AIndexSearch } from './IndexSearch'; -export class EmojiFloatingIndexSearch extends AIndexSearch { +export class EmojiFloatingIndexSearch extends AIndexSearch { protected static instance?: EmojiFloatingIndexSearch; private constructor(protected library: IEmojiLibrary) { @@ -16,8 +16,4 @@ export class EmojiFloatingIndexSearch extends AIndexSearch { return EmojiFloatingIndexSearch.instance; } - - protected transform(emoji: Emoji) { - return emoji; - } } diff --git a/packages/emoji/src/utils/IndexSearch/EmojiInlineIndexSearch.ts b/packages/emoji/src/utils/IndexSearch/EmojiInlineIndexSearch.ts index 8e5a71cc1d..f4c99f25b1 100644 --- a/packages/emoji/src/utils/IndexSearch/EmojiInlineIndexSearch.ts +++ b/packages/emoji/src/utils/IndexSearch/EmojiInlineIndexSearch.ts @@ -1,8 +1,4 @@ -import { - type Emoji, - EmojiInlineLibrary, - type IEmojiLibrary, -} from '../EmojiLibrary/index'; +import { EmojiInlineLibrary, type IEmojiLibrary } from '../EmojiLibrary/index'; import { AIndexSearch } from './IndexSearch'; export class EmojiInlineIndexSearch extends AIndexSearch { @@ -21,19 +17,4 @@ export class EmojiInlineIndexSearch extends AIndexSearch { return EmojiInlineIndexSearch.instance; } - - protected transform(emoji: Emoji) { - const { id, name, skins } = emoji; - - return { - data: { - emoji: skins[0].native, - id, - name, - text: name, - }, - key: id, - text: name, - }; - } } diff --git a/packages/emoji/src/utils/IndexSearch/IndexSearch.ts b/packages/emoji/src/utils/IndexSearch/IndexSearch.ts index ba038b4fa3..1643217789 100644 --- a/packages/emoji/src/utils/IndexSearch/IndexSearch.ts +++ b/packages/emoji/src/utils/IndexSearch/IndexSearch.ts @@ -1,21 +1,16 @@ -import type { TComboboxItem } from '@udecode/plate-combobox'; +import type { Emoji } from '@emoji-mart/data'; -import type { EmojiItemData } from '../../types'; -import type { Emoji, IEmojiLibrary } from '../EmojiLibrary/index'; +import type { IEmojiLibrary } from '../EmojiLibrary/index'; import { EMOJI_MAX_SEARCH_RESULT } from '../../constants'; -type IndexSearchReturnData = TComboboxItem; - -interface IIndexSearch { - get: () => R[]; +interface IIndexSearch { + get: () => Emoji[]; hasFound: () => boolean; search: (input: string) => void; } -export abstract class AIndexSearch - implements IIndexSearch -{ +export abstract class AIndexSearch implements IIndexSearch { protected input: string | undefined; protected maxResult = EMOJI_MAX_SEARCH_RESULT; protected result: string[] = []; @@ -58,7 +53,7 @@ export abstract class AIndexSearch for (const key of this.result) { const emoji = this.library?.getEmoji(key); - emojis.push(this.transform(emoji!)); + emojis.push(emoji); if (emojis.length >= this.maxResult) break; } @@ -66,7 +61,7 @@ export abstract class AIndexSearch return emojis; } - getEmoji(): RData | undefined { + getEmoji(): Emoji | undefined { return this.get()[0]; } @@ -92,6 +87,4 @@ export abstract class AIndexSearch return this; } - - protected abstract transform(emoji: Emoji): RData; } diff --git a/packages/emoji/src/utils/index.ts b/packages/emoji/src/utils/index.ts index 43343f4932..ef9c7cf89a 100644 --- a/packages/emoji/src/utils/index.ts +++ b/packages/emoji/src/utils/index.ts @@ -1,6 +1,6 @@ /** @file Automatically generated by barrelsby. */ -export * from './EmojiTriggeringController'; +export * from './insertEmoji'; export * from './EmojiLibrary/index'; export * from './EmojiPicker/index'; export * from './Grid/index'; diff --git a/packages/emoji/src/utils/insertEmoji.ts b/packages/emoji/src/utils/insertEmoji.ts new file mode 100644 index 0000000000..c101a647a1 --- /dev/null +++ b/packages/emoji/src/utils/insertEmoji.ts @@ -0,0 +1,30 @@ +import type { Emoji } from '@emoji-mart/data'; + +import { + type EElementOrText, + type PlateEditor, + type Value, + getPluginOptions, + insertNodes, +} from '@udecode/plate-common'; + +import type { EmojiPlugin } from '../types'; + +import { KEY_EMOJI } from '../createEmojiPlugin'; + +export const insertEmoji = < + V extends Value = Value, + E extends PlateEditor = PlateEditor, + TEmoji extends Emoji = Emoji, +>( + editor: E, + emoji: TEmoji +) => { + const { createEmojiNode } = getPluginOptions( + editor, + KEY_EMOJI + ); + + const emojiNode = createEmojiNode!(emoji); + insertNodes(editor, emojiNode as EElementOrText); +}; diff --git a/packages/emoji/src/withEmoji.ts b/packages/emoji/src/withEmoji.ts deleted file mode 100644 index bf64980a9d..0000000000 --- a/packages/emoji/src/withEmoji.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { comboboxActions } from '@udecode/plate-combobox'; -import { - type PlateEditor, - type Value, - type WithPlatePlugin, - isCollapsed, -} from '@udecode/plate-common/server'; - -import type { EmojiPlugin } from './types'; - -import { getEmojiOnSelectItem, getFindTriggeringInput } from './handlers/index'; -import { EmojiInlineIndexSearch } from './utils/index'; - -export const withEmoji = < - V extends Value = Value, - E extends PlateEditor = PlateEditor, ->( - editor: E, - { - options: { emojiTriggeringController, id }, - }: WithPlatePlugin -) => { - const emojiInlineIndexSearch = EmojiInlineIndexSearch.getInstance(); - - const findTheTriggeringInput = getFindTriggeringInput( - editor, - emojiTriggeringController! - ); - - const { apply, deleteBackward, deleteForward, insertText } = editor; - - editor.insertText = (char) => { - const { selection } = editor; - - if (!isCollapsed(selection)) { - return insertText(char); - } - - findTheTriggeringInput({ char }); - - return insertText(char); - }; - - editor.deleteBackward = (unit) => { - findTheTriggeringInput({ action: 'delete' }); - - return deleteBackward(unit); - }; - - editor.deleteForward = (unit) => { - findTheTriggeringInput(); - - return deleteForward(unit); - }; - - editor.apply = (operation) => { - apply(operation); - - if (!emojiTriggeringController?.hasTriggeringMark) { - return; - } - - const searchText = emojiTriggeringController.getText(); - - switch (operation.type) { - case 'set_selection': { - emojiTriggeringController.reset(); - comboboxActions.reset(); - - break; - } - case 'insert_text': { - if ( - emojiTriggeringController.hasEnclosingTriggeringMark() && - emojiInlineIndexSearch.search(searchText).hasFound(true) - ) { - const item = emojiInlineIndexSearch.getEmoji(); - item && getEmojiOnSelectItem()(editor, item); - - break; - } - if ( - !emojiTriggeringController.hasEnclosingTriggeringMark() && - emojiTriggeringController.isTriggering && - emojiInlineIndexSearch.search(searchText).hasFound() - ) { - comboboxActions.items( - emojiInlineIndexSearch.search(searchText).get() - ); - comboboxActions.open({ - activeId: id!, - targetRange: editor.selection, - text: '', - }); - - break; - } - - emojiTriggeringController.reset(); - comboboxActions.reset(); - - break; - } - case 'remove_text': { - if ( - emojiTriggeringController.isTriggering && - emojiInlineIndexSearch.search(searchText).hasFound() - ) { - comboboxActions.items( - emojiInlineIndexSearch.search(searchText).get() - ); - comboboxActions.open({ - activeId: id!, - targetRange: editor.selection, - text: '', - }); - - break; - } - - emojiTriggeringController.reset(); - comboboxActions.reset(); - - break; - } - } - }; - - return editor; -}; diff --git a/packages/slash-command/src/createSlashPlugin.ts b/packages/slash-command/src/createSlashPlugin.ts index bd901b0468..88315ca7bc 100644 --- a/packages/slash-command/src/createSlashPlugin.ts +++ b/packages/slash-command/src/createSlashPlugin.ts @@ -5,7 +5,6 @@ import { import { createPluginFactory } from '@udecode/plate-common/server'; export const KEY_SLASH_COMMAND = 'slash_command'; - export const ELEMENT_SLASH_INPUT = 'slash_input'; export const createSlashPlugin = createPluginFactory({ From 27616fec438ce05c07ae15d5bfbe1a96b3b9bcb5 Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Mon, 3 Jun 2024 17:13:17 +0100 Subject: [PATCH 37/63] Delete old combobox code --- packages/combobox/src/index.ts | 1 - .../combobox.store.ts | 133 ------------------ .../createComboboxPlugin.ts | 14 -- .../legacy-combobox-delete-me/hooks/index.ts | 5 - .../hooks/useComboboxContent.ts | 81 ----------- .../hooks/useComboboxControls.ts | 38 ----- .../hooks/useComboboxItem.tsx | 60 -------- .../src/legacy-combobox-delete-me/index.ts | 9 -- .../onChangeCombobox.spec.tsx | 128 ----------------- .../onChangeCombobox.ts | 81 ----------- .../onKeyDownCombobox.ts | 83 ----------- .../types/ComboboxOnSelectItem.ts | 35 ----- .../types/ComboboxProps.ts | 32 ----- .../legacy-combobox-delete-me/types/index.ts | 4 - .../utils/getNextNonDisabledIndex.ts | 52 ------- .../utils/getNextWrappingIndex.ts | 58 -------- .../utils/getTextFromTrigger.ts | 95 ------------- .../legacy-combobox-delete-me/utils/index.ts | 5 - 18 files changed, 914 deletions(-) delete mode 100644 packages/combobox/src/legacy-combobox-delete-me/combobox.store.ts delete mode 100644 packages/combobox/src/legacy-combobox-delete-me/createComboboxPlugin.ts delete mode 100644 packages/combobox/src/legacy-combobox-delete-me/hooks/index.ts delete mode 100644 packages/combobox/src/legacy-combobox-delete-me/hooks/useComboboxContent.ts delete mode 100644 packages/combobox/src/legacy-combobox-delete-me/hooks/useComboboxControls.ts delete mode 100644 packages/combobox/src/legacy-combobox-delete-me/hooks/useComboboxItem.tsx delete mode 100644 packages/combobox/src/legacy-combobox-delete-me/index.ts delete mode 100644 packages/combobox/src/legacy-combobox-delete-me/onChangeCombobox.spec.tsx delete mode 100644 packages/combobox/src/legacy-combobox-delete-me/onChangeCombobox.ts delete mode 100644 packages/combobox/src/legacy-combobox-delete-me/onKeyDownCombobox.ts delete mode 100644 packages/combobox/src/legacy-combobox-delete-me/types/ComboboxOnSelectItem.ts delete mode 100644 packages/combobox/src/legacy-combobox-delete-me/types/ComboboxProps.ts delete mode 100644 packages/combobox/src/legacy-combobox-delete-me/types/index.ts delete mode 100644 packages/combobox/src/legacy-combobox-delete-me/utils/getNextNonDisabledIndex.ts delete mode 100644 packages/combobox/src/legacy-combobox-delete-me/utils/getNextWrappingIndex.ts delete mode 100644 packages/combobox/src/legacy-combobox-delete-me/utils/getTextFromTrigger.ts delete mode 100644 packages/combobox/src/legacy-combobox-delete-me/utils/index.ts diff --git a/packages/combobox/src/index.ts b/packages/combobox/src/index.ts index a906a917b3..df6e22525a 100644 --- a/packages/combobox/src/index.ts +++ b/packages/combobox/src/index.ts @@ -3,5 +3,4 @@ export * from './types'; export * from './withTriggerCombobox'; export * from './hooks/index'; -export * from './legacy-combobox-delete-me/index'; export * from './utils/index'; diff --git a/packages/combobox/src/legacy-combobox-delete-me/combobox.store.ts b/packages/combobox/src/legacy-combobox-delete-me/combobox.store.ts deleted file mode 100644 index 67ffa69b85..0000000000 --- a/packages/combobox/src/legacy-combobox-delete-me/combobox.store.ts +++ /dev/null @@ -1,133 +0,0 @@ -import type { Range } from 'slate'; - -import { - type ZustandStateActions, - type ZustandStoreApi, - createZustandStore, -} from '@udecode/plate-common/server'; - -import type { ComboboxOnSelectItem, NoData, TComboboxItem } from './types'; - -export type ComboboxStateById = { - /** Is opening/closing the combobox controlled by the client. */ - controlled?: boolean; - - /** - * Items filter function by text. - * - * @default (value) => value.text.toLowerCase().startsWith(search.toLowerCase()) - */ - filter?: (search: string) => (item: TComboboxItem) => boolean; - - /** Combobox id. */ - id: string; - - /** - * Max number of items. - * - * @default items.length - */ - maxSuggestions?: number; - - /** Called when an item is selected. */ - onSelectItem: ComboboxOnSelectItem | null; - - /** Regular expression for search, for example to allow whitespace */ - searchPattern?: string; - - /** Sort filtered items before applying maxSuggestions. */ - sort?: ( - search: string - ) => (a: TComboboxItem, b: TComboboxItem) => number; - - /** Trigger that activates the combobox. */ - trigger: string; -}; - -export type ComboboxStoreById = ZustandStoreApi< - string, - ComboboxStateById, - ZustandStateActions> ->; - -export type ComboboxState = { - /** Active id (combobox id which is opened). */ - activeId: null | string; - - /** - * Object whose keys are combobox ids and values are config stores (e.g. one - * for tag, one for mention,...). - */ - byId: Record; - - /** Filtered items */ - filteredItems: TComboboxItem[]; - - /** Highlighted index. */ - highlightedIndex: number; - - /** Unfiltered items. */ - items: TComboboxItem[]; - - /** Range from the trigger to the cursor. */ - targetRange: Range | null; - - /** Text after the trigger. */ - text: null | string; -}; - -const createComboboxStore = (state: ComboboxStateById) => - createZustandStore(`combobox-${state.id}`)(state); - -export const comboboxStore = createZustandStore('combobox')({ - activeId: null, - byId: {}, - filteredItems: [], - highlightedIndex: 0, - items: [], - targetRange: null, - text: null, -}) - .extendActions((set, get) => ({ - open: (state: Pick) => { - set.mergeState(state); - }, - reset: () => { - set.state((draft) => { - draft.activeId = null; - draft.highlightedIndex = 0; - draft.filteredItems = []; - draft.items = []; - draft.text = null; - draft.targetRange = null; - }); - }, - setComboboxById: (state: ComboboxStateById) => { - if (get.byId()[state.id]) return; - - set.state((draft) => { - draft.byId[state.id] = createComboboxStore( - state as unknown as ComboboxStateById - ); - }); - }, - })) - .extendSelectors((state) => ({ - isOpen: () => !!state.activeId, - })); - -export const useComboboxSelectors = comboboxStore.use; - -export const comboboxSelectors = comboboxStore.get; - -export const comboboxActions = comboboxStore.set; - -export const getComboboxStoreById = (id: null | string) => - id ? comboboxSelectors.byId()[id] : null; - -export const useActiveComboboxStore = () => { - const activeId = useComboboxSelectors.activeId(); - const comboboxes = useComboboxSelectors.byId(); - - return activeId ? comboboxes[activeId] : null; -}; diff --git a/packages/combobox/src/legacy-combobox-delete-me/createComboboxPlugin.ts b/packages/combobox/src/legacy-combobox-delete-me/createComboboxPlugin.ts deleted file mode 100644 index 327dffca5c..0000000000 --- a/packages/combobox/src/legacy-combobox-delete-me/createComboboxPlugin.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { createPluginFactory } from '@udecode/plate-common/server'; - -import { onChangeCombobox } from './onChangeCombobox'; -import { onKeyDownCombobox } from './onKeyDownCombobox'; - -export const KEY_COMBOBOX = 'combobox'; - -export const createComboboxPlugin = createPluginFactory({ - handlers: { - onChange: onChangeCombobox, - onKeyDown: onKeyDownCombobox, - }, - key: KEY_COMBOBOX, -}); diff --git a/packages/combobox/src/legacy-combobox-delete-me/hooks/index.ts b/packages/combobox/src/legacy-combobox-delete-me/hooks/index.ts deleted file mode 100644 index 21604fd34b..0000000000 --- a/packages/combobox/src/legacy-combobox-delete-me/hooks/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** @file Automatically generated by barrelsby. */ - -export * from './useComboboxContent'; -export * from './useComboboxControls'; -export * from './useComboboxItem'; diff --git a/packages/combobox/src/legacy-combobox-delete-me/hooks/useComboboxContent.ts b/packages/combobox/src/legacy-combobox-delete-me/hooks/useComboboxContent.ts deleted file mode 100644 index 446a3981ae..0000000000 --- a/packages/combobox/src/legacy-combobox-delete-me/hooks/useComboboxContent.ts +++ /dev/null @@ -1,81 +0,0 @@ -import React from 'react'; - -import type { ComboboxProps } from '../types/ComboboxProps'; - -import { - type ComboboxControls, - type Data, - type NoData, - comboboxActions, - useActiveComboboxStore, - useComboboxSelectors, -} from '..'; - -export type ComboboxContentProps = { - combobox: ComboboxControls; -} & Omit< - ComboboxProps, - | 'controlled' - | 'filter' - | 'id' - | 'maxSuggestions' - | 'onSelectItem' - | 'searchPattern' - | 'sort' - | 'trigger' ->; - -export type ComboboxContentRootProps = { - combobox: ComboboxControls; -} & ComboboxContentProps; - -export const useComboboxContentState = ({ - combobox, - items, -}: ComboboxContentRootProps) => { - const targetRange = useComboboxSelectors.targetRange(); - const activeComboboxStore = useActiveComboboxStore()!; - const text = useComboboxSelectors.text() ?? ''; - const storeItems = useComboboxSelectors.items(); - const filter = activeComboboxStore.use.filter?.(); - const sort = activeComboboxStore.use.sort?.(); - const maxSuggestions = - activeComboboxStore.use.maxSuggestions?.() ?? storeItems.length; - - // Update items - React.useEffect(() => { - items && comboboxActions.items(items); - }, [items]); - - // Filter items - React.useEffect(() => { - comboboxActions.filteredItems( - storeItems - .filter( - filter - ? filter(text) - : (value) => value.text.toLowerCase().startsWith(text.toLowerCase()) - ) - .sort(sort?.(text)) - .slice(0, maxSuggestions) - ); - }, [filter, sort, storeItems, maxSuggestions, text]); - - return { - combobox, - targetRange, - }; -}; - -export const useComboboxContent = ( - state: ReturnType -) => { - const menuProps = state.combobox - ? state.combobox.getMenuProps({}, { suppressRefError: true }) - : { ref: null }; - - return { - menuProps, - targetRange: state.targetRange, - }; -}; diff --git a/packages/combobox/src/legacy-combobox-delete-me/hooks/useComboboxControls.ts b/packages/combobox/src/legacy-combobox-delete-me/hooks/useComboboxControls.ts deleted file mode 100644 index ac56285a2a..0000000000 --- a/packages/combobox/src/legacy-combobox-delete-me/hooks/useComboboxControls.ts +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react'; - -import { useCombobox } from 'downshift'; - -import { useComboboxSelectors } from '../combobox.store'; - -export type ComboboxControls = ReturnType; - -export const useComboboxControls = () => { - const isOpen = useComboboxSelectors.isOpen(); - const highlightedIndex = useComboboxSelectors.highlightedIndex(); - const filteredItems = useComboboxSelectors.filteredItems(); - - const { - closeMenu, - getComboboxProps, - getInputProps, - getItemProps, - getMenuProps, - } = useCombobox({ - circularNavigation: true, - highlightedIndex, - isOpen, - items: filteredItems, - }); - getMenuProps({}, { suppressRefError: true }); - getComboboxProps({}, { suppressRefError: true }); - getInputProps({}, { suppressRefError: true }); - - return React.useMemo( - () => ({ - closeMenu, - getItemProps, - getMenuProps, - }), - [closeMenu, getItemProps, getMenuProps] - ); -}; diff --git a/packages/combobox/src/legacy-combobox-delete-me/hooks/useComboboxItem.tsx b/packages/combobox/src/legacy-combobox-delete-me/hooks/useComboboxItem.tsx deleted file mode 100644 index 5073df175c..0000000000 --- a/packages/combobox/src/legacy-combobox-delete-me/hooks/useComboboxItem.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { useEditorRef } from '@udecode/plate-common'; - -import type { ComboboxContentProps } from './useComboboxContent'; - -import { - type ComboboxControls, - type Data, - type NoData, - type TComboboxItem, - comboboxSelectors, - getComboboxStoreById, - useComboboxSelectors, -} from '..'; - -export type ComboboxContentItemProps = { - combobox: ComboboxControls; - index: number; - item: TComboboxItem; -} & Pick, 'onRenderItem'>; - -export interface ComboboxItemProps { - item: TComboboxItem; - search: string; -} - -export const useComboboxItem = ({ - combobox, - index, - item, - onRenderItem, -}: ComboboxContentItemProps) => { - const editor = useEditorRef(); - const text = useComboboxSelectors.text() ?? ''; - const highlightedIndex = useComboboxSelectors.highlightedIndex(); - - const Item = onRenderItem - ? onRenderItem({ item: item as TComboboxItem, search: text }) - : item.text; - - const highlighted = index === highlightedIndex; - - return { - props: { - 'data-highlighted': highlighted, - ...combobox.getItemProps({ - index, - item, - }), - children: Item, - onMouseDown: (e: React.MouseEvent) => { - e.preventDefault(); - - const onSelectItem = getComboboxStoreById( - comboboxSelectors.activeId() - )?.get.onSelectItem(); - onSelectItem?.(editor, item); - }, - }, - }; -}; diff --git a/packages/combobox/src/legacy-combobox-delete-me/index.ts b/packages/combobox/src/legacy-combobox-delete-me/index.ts deleted file mode 100644 index 020be02fe9..0000000000 --- a/packages/combobox/src/legacy-combobox-delete-me/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** @file Automatically generated by barrelsby. */ - -export * from './combobox.store'; -export * from './createComboboxPlugin'; -export * from './onChangeCombobox'; -export * from './onKeyDownCombobox'; -export * from './hooks/index'; -export * from './types/index'; -export * from './utils/index'; diff --git a/packages/combobox/src/legacy-combobox-delete-me/onChangeCombobox.spec.tsx b/packages/combobox/src/legacy-combobox-delete-me/onChangeCombobox.spec.tsx deleted file mode 100644 index 10ec16426f..0000000000 --- a/packages/combobox/src/legacy-combobox-delete-me/onChangeCombobox.spec.tsx +++ /dev/null @@ -1,128 +0,0 @@ -/** @jsx jsx */ - -import { - type HandlerReturnType, - createPlateEditor, -} from '@udecode/plate-common'; -import { getMentionOnSelectItem } from '@udecode/plate-mention'; -import { createParagraphPlugin } from '@udecode/plate-paragraph'; -import { jsx } from '@udecode/plate-test-utils'; - -import { - type ComboboxState, - comboboxActions, - comboboxSelectors, -} from './combobox.store'; -import { createComboboxPlugin } from './createComboboxPlugin'; -import { onChangeCombobox } from './onChangeCombobox'; - -jsx; - -describe('onChangeCombobox', () => { - const createEditor = (state: React.ReactElement) => { - const plugins = [createParagraphPlugin(), createComboboxPlugin()]; - - return createPlateEditor({ - editor: ({state}) as any, - plugins, - }); - }; - - const onChange = (fragment: React.ReactElement): HandlerReturnType => { - return onChangeCombobox(createEditor(fragment))(); - }; - - const createCombobox = ({ - controlled = false, - trigger = '@', - id = trigger, - searchPattern = '\\S+', - }: { - controlled?: boolean; - id?: string; - searchPattern?: string; - trigger?: string; - } = {}) => - comboboxActions.setComboboxById({ - controlled, - id, - onSelectItem: getMentionOnSelectItem({ key: id }), - searchPattern, - trigger, - }); - - beforeEach(() => { - comboboxActions.byId({}); - comboboxActions.reset(); - }); - - it('should open the combobox if the text after trigger matches pattern', () => { - createCombobox(); - - onChange( - - @hello - - - ); - - expect(comboboxSelectors.state()).toMatchObject>({ - activeId: expect.anything(), - text: 'hello', - }); - }); - - it('should not open the combobox if the combobox is controlled', () => { - createCombobox({ controlled: true }); - - onChange( - - @hello - - - ); - - expect(comboboxSelectors.state()).toMatchObject>({ - activeId: null, - }); - }); - - it('should not alter the state of a controlled combobox', () => { - const id = 'controlled'; - - createCombobox({ controlled: true, id }); - - comboboxActions.open({ - activeId: id, - targetRange: null, - text: '', - }); - - onChange( - - - - ); - - expect(comboboxSelectors.state()).toMatchObject>({ - activeId: id, - }); - }); - - it('should handle a mix of controlled and uncontrolled comboboxes', () => { - createCombobox({ controlled: true, trigger: '@' }); - createCombobox({ controlled: false, trigger: '#' }); - - onChange( - - #hello - - - ); - - expect(comboboxSelectors.state()).toMatchObject>({ - activeId: expect.anything(), - text: 'hello', - }); - }); -}); diff --git a/packages/combobox/src/legacy-combobox-delete-me/onChangeCombobox.ts b/packages/combobox/src/legacy-combobox-delete-me/onChangeCombobox.ts deleted file mode 100644 index e58a45f01b..0000000000 --- a/packages/combobox/src/legacy-combobox-delete-me/onChangeCombobox.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { - type PlateEditor, - type Value, - isCollapsed, -} from '@udecode/plate-common/server'; -import { Range } from 'slate'; - -import { comboboxActions, comboboxSelectors } from './combobox.store'; -import { getTextFromTrigger } from './utils/getTextFromTrigger'; - -/** - * For each combobox state (byId): - * - * - If the selection is collapsed - * - If the cursor follows the trigger - * - If there is text without whitespaces after the trigger - * - Open the combobox: set id, search, targetRange in the store Close the - * combobox if needed - */ -export const onChangeCombobox = - = PlateEditor>( - editor: E - ) => - () => { - const byId = comboboxSelectors.byId(); - const activeId = comboboxSelectors.activeId(); - - let shouldClose = true; - - for (const store of Object.values(byId)) { - const id = store.get.id(); - const controlled = store.get.controlled?.(); - - if (controlled) { - // do not close controlled comboboxes - if (activeId === id) { - shouldClose = false; - - break; - } else { - // do not open controlled comboboxes - continue; - } - } - - const { selection } = editor; - - if (!selection || !isCollapsed(selection)) { - continue; - } - - const trigger = store.get.trigger(); - const searchPattern = store.get.searchPattern?.(); - - const isCursorAfterTrigger = getTextFromTrigger(editor, { - at: Range.start(selection), - searchPattern, - trigger, - }); - - if (!isCursorAfterTrigger) { - continue; - } - - const { range, textAfterTrigger } = isCursorAfterTrigger; - - comboboxActions.open({ - activeId: id, - targetRange: range, - text: textAfterTrigger, - }); - - shouldClose = false; - - break; - } - - if (shouldClose && comboboxSelectors.isOpen()) { - comboboxActions.reset(); - } - }; diff --git a/packages/combobox/src/legacy-combobox-delete-me/onKeyDownCombobox.ts b/packages/combobox/src/legacy-combobox-delete-me/onKeyDownCombobox.ts deleted file mode 100644 index a0770d9e7d..0000000000 --- a/packages/combobox/src/legacy-combobox-delete-me/onKeyDownCombobox.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { Hotkeys } from '@udecode/plate-common'; -import { - type KeyboardHandlerReturnType, - type PlateEditor, - type Value, - isHotkey, -} from '@udecode/plate-common/server'; - -import { - comboboxActions, - comboboxSelectors, - getComboboxStoreById, -} from './combobox.store'; -import { getNextWrappingIndex } from './utils/getNextWrappingIndex'; - -/** - * If the combobox is open, handle: - * - * - Down (next item) - * - Up (previous item) - * - Escape (reset combobox) - * - Tab, enter (select item) - */ -export const onKeyDownCombobox = - = PlateEditor>( - editor: E - ): KeyboardHandlerReturnType => - (event) => { - const { activeId, filteredItems, highlightedIndex } = - comboboxSelectors.state(); - const isOpen = comboboxSelectors.isOpen(); - - if (!isOpen) return; - - const store = getComboboxStoreById(activeId); - - if (!store) return; - - const onSelectItem = store.get.onSelectItem(); - - if (isHotkey('down', event)) { - event.preventDefault(); - - const newIndex = getNextWrappingIndex( - 1, - highlightedIndex, - filteredItems.length, - () => {}, - true - ); - comboboxActions.highlightedIndex(newIndex); - - return; - } - if (isHotkey('up', event)) { - event.preventDefault(); - - const newIndex = getNextWrappingIndex( - -1, - highlightedIndex, - filteredItems.length, - () => {}, - true - ); - comboboxActions.highlightedIndex(newIndex); - - return; - } - if (isHotkey('escape', event)) { - event.preventDefault(); - comboboxActions.reset(); - - return; - } - if (Hotkeys.isTab(editor, event) || isHotkey('enter', event)) { - event.preventDefault(); - event.stopPropagation(); - - if (filteredItems[highlightedIndex]) { - onSelectItem?.(editor, filteredItems[highlightedIndex]); - } - } - }; diff --git a/packages/combobox/src/legacy-combobox-delete-me/types/ComboboxOnSelectItem.ts b/packages/combobox/src/legacy-combobox-delete-me/types/ComboboxOnSelectItem.ts deleted file mode 100644 index 362ffb07e1..0000000000 --- a/packages/combobox/src/legacy-combobox-delete-me/types/ComboboxOnSelectItem.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { PlateEditor, Value } from '@udecode/plate-common/server'; - -export interface TComboboxItemBase { - /** Unique key. */ - key: string; - - /** Item text. */ - text: any; - - /** - * Whether the item is disabled. - * - * @default false - */ - disabled?: boolean; -} - -export interface TComboboxItemWithData - extends TComboboxItemBase { - /** Data available to `onRenderItem`. */ - data: TData; -} - -export type NoData = undefined; - -export type Data = unknown; - -export type TComboboxItem = TData extends NoData - ? TComboboxItemBase - : TComboboxItemWithData; - -export type ComboboxOnSelectItem = ( - editor: PlateEditor, - item: TComboboxItem -) => any; diff --git a/packages/combobox/src/legacy-combobox-delete-me/types/ComboboxProps.ts b/packages/combobox/src/legacy-combobox-delete-me/types/ComboboxProps.ts deleted file mode 100644 index db50c6810d..0000000000 --- a/packages/combobox/src/legacy-combobox-delete-me/types/ComboboxProps.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type React from 'react'; - -import type { - ComboboxItemProps, - ComboboxState, - ComboboxStateById, - ComboboxStoreById, - NoData, -} from '..'; - -export interface ComboboxProps - extends Partial, 'items'>>, - ComboboxStateById { - /** Render this component when the combobox is open (useful to inject hooks). */ - component?: React.FC<{ store: ComboboxStoreById }>; - - /** - * Whether to hide the combobox. - * - * @default !items.length - */ - disabled?: boolean; - - /** - * Render combobox item. - * - * @default text - */ - onRenderItem?: React.FC>; - - portalElement?: HTMLElement; -} diff --git a/packages/combobox/src/legacy-combobox-delete-me/types/index.ts b/packages/combobox/src/legacy-combobox-delete-me/types/index.ts deleted file mode 100644 index e9388cfb53..0000000000 --- a/packages/combobox/src/legacy-combobox-delete-me/types/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -/** @file Automatically generated by barrelsby. */ - -export * from './ComboboxOnSelectItem'; -export * from './ComboboxProps'; diff --git a/packages/combobox/src/legacy-combobox-delete-me/utils/getNextNonDisabledIndex.ts b/packages/combobox/src/legacy-combobox-delete-me/utils/getNextNonDisabledIndex.ts deleted file mode 100644 index 1f6b8f9e43..0000000000 --- a/packages/combobox/src/legacy-combobox-delete-me/utils/getNextNonDisabledIndex.ts +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Returns the next index in the list of an item that is not disabled. - * - * @param {number} moveAmount Number of positions to move. Negative to move - * backwards, positive forwards. - * @param {number} baseIndex The initial position to move from. - * @param {number} itemCount The total number of items. - * @param {Function} getItemNodeFromIndex Used to check if item is disabled. - * @param {boolean} circular Specify if navigation is circular. Default is true. - * @returns {number} The new index. Returns baseIndex if item is not disabled. - * Returns next non-disabled item otherwise. If no non-disabled found it will - * return -1. - */ -export const getNextNonDisabledIndex = ( - moveAmount: number, - baseIndex: number, - itemCount: number, - getItemNodeFromIndex: any, - circular: boolean -): number => { - const currentElementNode = getItemNodeFromIndex(baseIndex); - - if (!currentElementNode?.hasAttribute('disabled')) { - return baseIndex; - } - if (moveAmount > 0) { - for (let index = baseIndex + 1; index < itemCount; index++) { - if (!getItemNodeFromIndex(index).hasAttribute('disabled')) { - return index; - } - } - } else { - for (let index = baseIndex - 1; index >= 0; index--) { - if (!getItemNodeFromIndex(index).hasAttribute('disabled')) { - return index; - } - } - } - if (circular) { - return moveAmount > 0 - ? getNextNonDisabledIndex(1, 0, itemCount, getItemNodeFromIndex, false) - : getNextNonDisabledIndex( - -1, - itemCount - 1, - itemCount, - getItemNodeFromIndex, - false - ); - } - - return -1; -}; diff --git a/packages/combobox/src/legacy-combobox-delete-me/utils/getNextWrappingIndex.ts b/packages/combobox/src/legacy-combobox-delete-me/utils/getNextWrappingIndex.ts deleted file mode 100644 index 4371497ac5..0000000000 --- a/packages/combobox/src/legacy-combobox-delete-me/utils/getNextWrappingIndex.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { getNextNonDisabledIndex } from './getNextNonDisabledIndex'; - -/** - * Returns the new index in the list, in a circular way. If next value is out of - * bonds from the total, it will wrap to either 0 or itemCount - 1. - * - * @param {number} moveAmount Number of positions to move. Negative to move - * backwards, positive forwards. - * @param {number} baseIndex The initial position to move from. - * @param {number} itemCount The total number of items. - * @param {Function} getItemNodeFromIndex Used to check if item is disabled. - * @param {boolean} circular Specify if navigation is circular. Default is true. - * @returns {number} The new index after the move. - */ -export const getNextWrappingIndex = ( - moveAmount: number, - baseIndex: number, - itemCount: number, - getItemNodeFromIndex: any, - circular = true -) => { - if (itemCount === 0) { - return -1; - } - - const itemsLastIndex = itemCount - 1; - - // noinspection SuspiciousTypeOfGuard - if ( - typeof baseIndex !== 'number' || - baseIndex < 0 || - baseIndex >= itemCount - ) { - baseIndex = moveAmount > 0 ? -1 : itemsLastIndex + 1; - } - - let newIndex = baseIndex + moveAmount; - - if (newIndex < 0) { - newIndex = circular ? itemsLastIndex : 0; - } else if (newIndex > itemsLastIndex) { - newIndex = circular ? 0 : itemsLastIndex; - } - - const nonDisabledNewIndex = getNextNonDisabledIndex( - moveAmount, - newIndex, - itemCount, - getItemNodeFromIndex, - circular - ); - - if (nonDisabledNewIndex === -1) { - return baseIndex >= itemCount ? -1 : baseIndex; - } - - return nonDisabledNewIndex; -}; diff --git a/packages/combobox/src/legacy-combobox-delete-me/utils/getTextFromTrigger.ts b/packages/combobox/src/legacy-combobox-delete-me/utils/getTextFromTrigger.ts deleted file mode 100644 index 2d73472d90..0000000000 --- a/packages/combobox/src/legacy-combobox-delete-me/utils/getTextFromTrigger.ts +++ /dev/null @@ -1,95 +0,0 @@ -import type { Point } from 'slate'; - -import { - type TEditor, - type Value, - escapeRegExp, - getEditorString, - getPointBefore, - getRange, -} from '@udecode/plate-common/server'; - -/** - * Get text and range from trigger to cursor. Starts with trigger and ends with - * non-whitespace character. - */ -export const getTextFromTrigger = ( - editor: TEditor, - { - at, - searchPattern = `\\S+`, - trigger, - }: { at: Point; searchPattern?: string; trigger: string } -) => { - const escapedTrigger = escapeRegExp(trigger); - const triggerRegex = new RegExp(`(?:^|\\s)${escapedTrigger}`); - - let start: Point | undefined = at; - let end: Point | undefined; - - // eslint-disable-next-line no-constant-condition - while (true) { - end = start; - - if (!start) break; - - start = getPointBefore(editor, start); - const charRange = start && getRange(editor, start, end); - const charText = getEditorString(editor, charRange); - - if (!charText.match(searchPattern)) { - start = end; - - break; - } - } - - // Range from start to cursor - const range = start && getRange(editor, start, at); - const text = getEditorString(editor, range); - - if (!range || !text.match(triggerRegex)) return; - - return { - range, - textAfterTrigger: text.slice(trigger.length), - }; -}; - -// export const matchesTriggerAndPattern = ( -// editor: TEditor, -// { at, trigger, pattern }: { at: Point; trigger: string; pattern: string } -// ) => { -// // Point at the start of line -// const lineStart = getPointBefore(editor, at, { unit: 'line' }); -// -// // Range from before to start -// const beforeRange = lineStart && getRange(editor, lineStart, at); -// -// // Before text -// const beforeText = getEditorString(editor, beforeRange); -// -// // Starts with char and ends with word characters -// const escapedTrigger = escapeRegExp(trigger); -// -// const beforeRegex = new RegExp(`(?:^|\\s)${escapedTrigger}(${pattern})$`); -// -// // Match regex on before text -// const match = !!beforeText && beforeText.match(beforeRegex); -// -// // Point at the start of mention -// const mentionStart = match -// ? getPointBefore(editor, at, { -// unit: 'character', -// distance: match[1].length + trigger.length, -// }) -// : null; -// -// // Range from mention to start -// const mentionRange = mentionStart && getRange(editor, mentionStart, at); -// -// return { -// range: mentionRange, -// match, -// }; -// }; diff --git a/packages/combobox/src/legacy-combobox-delete-me/utils/index.ts b/packages/combobox/src/legacy-combobox-delete-me/utils/index.ts deleted file mode 100644 index 1864c7ebf3..0000000000 --- a/packages/combobox/src/legacy-combobox-delete-me/utils/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** @file Automatically generated by barrelsby. */ - -export * from './getNextNonDisabledIndex'; -export * from './getNextWrappingIndex'; -export * from './getTextFromTrigger'; From af87f807a79966475c535d26211d7f6053582040 Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Mon, 3 Jun 2024 17:26:53 +0100 Subject: [PATCH 38/63] Remove old combobox component --- apps/www/public/registry/index.json | 13 -- apps/www/src/__registry__/index.tsx | 7 - apps/www/src/config/customizer-items.ts | 11 -- apps/www/src/config/customizer-list.ts | 3 - apps/www/src/config/customizer-plugins.ts | 7 - apps/www/src/config/descriptions.ts | 2 - .../lib/plate/demo/plugins/imagePlugins.ts | 2 - .../default/example/playground-demo.tsx | 2 - .../registry/default/plate-ui/combobox.tsx | 160 ------------------ .../default/plate-ui/emoji-input-element.tsx | 1 + .../default/plate-ui/inline-combobox.tsx | 15 +- .../plate-ui/mention-input-element.tsx | 1 + .../default/plate-ui/slash-input-element.tsx | 2 +- apps/www/src/registry/registry.ts | 11 -- 14 files changed, 17 insertions(+), 220 deletions(-) delete mode 100644 apps/www/src/registry/default/plate-ui/combobox.tsx diff --git a/apps/www/public/registry/index.json b/apps/www/public/registry/index.json index 83a569a60d..0dcc277bb8 100644 --- a/apps/www/public/registry/index.json +++ b/apps/www/public/registry/index.json @@ -234,19 +234,6 @@ ], "type": "components:plate-ui" }, - { - "name": "combobox", - "dependencies": [ - "@radix-ui/react-popover", - "@udecode/plate-combobox", - "@udecode/plate-floating" - ], - "registryDependencies": [], - "files": [ - "plate-ui/combobox.tsx" - ], - "type": "components:plate-ui" - }, { "name": "command", "dependencies": [ diff --git a/apps/www/src/__registry__/index.tsx b/apps/www/src/__registry__/index.tsx index 08c957b105..f4734a4969 100644 --- a/apps/www/src/__registry__/index.tsx +++ b/apps/www/src/__registry__/index.tsx @@ -319,13 +319,6 @@ export const Index: Record = { files: ['registry/default/plate-ui/code-syntax-leaf.tsx'], component: React.lazy(() => import('@/registry/default/plate-ui/code-syntax-leaf')), }, - 'combobox': { - name: 'combobox', - type: 'components:plate-ui', - registryDependencies: [], - files: ['registry/default/plate-ui/combobox.tsx'], - component: React.lazy(() => import('@/registry/default/plate-ui/combobox')), - }, 'command': { name: 'command', type: 'components:plate-ui', diff --git a/apps/www/src/config/customizer-items.ts b/apps/www/src/config/customizer-items.ts index 222042f0a1..a4f1d85db4 100644 --- a/apps/www/src/config/customizer-items.ts +++ b/apps/www/src/config/customizer-items.ts @@ -17,7 +17,6 @@ import { } from '@udecode/plate-break'; import { KEY_CAPTION } from '@udecode/plate-caption'; import { ELEMENT_CODE_BLOCK } from '@udecode/plate-code-block'; -import { KEY_COMBOBOX } from '@udecode/plate-combobox'; import { MARK_COMMENT } from '@udecode/plate-comments'; import { KEY_DND } from '@udecode/plate-dnd'; import { KEY_EMOJI } from '@udecode/plate-emoji'; @@ -260,7 +259,6 @@ export const customizerItems: Record = { usage: 'MentionCombobox', }, ], - dependencies: [KEY_COMBOBOX], id: ELEMENT_MENTION, label: 'Mention', npmPackage: '@udecode/plate-mention', @@ -425,14 +423,6 @@ export const customizerItems: Record = { ], route: customizerPlugins.media.route, }, - [KEY_COMBOBOX]: { - badges: [customizerBadges.handler, customizerBadges.ui], - id: KEY_COMBOBOX, - label: 'Combobox', - npmPackage: '@udecode/plate-combobox', - pluginFactory: 'createComboboxPlugin', - route: customizerPlugins.combobox.route, - }, [KEY_DELETE]: { badges: [customizerBadges.handler], id: KEY_DELETE, @@ -508,7 +498,6 @@ export const customizerItems: Record = { // usage: 'EmojiCombobox', // }, ], - dependencies: [KEY_COMBOBOX], id: KEY_EMOJI, label: 'Emoji', npmPackage: '@udecode/plate-emoji', diff --git a/apps/www/src/config/customizer-list.ts b/apps/www/src/config/customizer-list.ts index 712ad9a9f4..42bfab67ca 100644 --- a/apps/www/src/config/customizer-list.ts +++ b/apps/www/src/config/customizer-list.ts @@ -17,7 +17,6 @@ import { } from '@udecode/plate-break'; import { KEY_CAPTION } from '@udecode/plate-caption'; import { ELEMENT_CODE_BLOCK } from '@udecode/plate-code-block'; -import { KEY_COMBOBOX } from '@udecode/plate-combobox'; import { MARK_COMMENT } from '@udecode/plate-comments'; import { KEY_DND } from '@udecode/plate-dnd'; import { KEY_EMOJI } from '@udecode/plate-emoji'; @@ -109,7 +108,6 @@ export const customizerList = [ customizerItems[KEY_AUTOFORMAT], customizerItems[KEY_BLOCK_SELECTION], customizerItems[KEY_CAPTION], - customizerItems[KEY_COMBOBOX], customizerItems[KEY_DND], customizerItems[KEY_DRAG_OVER_CURSOR], customizerItems[KEY_EMOJI], @@ -178,7 +176,6 @@ export const orderedPluginKeys = [ // Functionality KEY_AUTOFORMAT, KEY_BLOCK_SELECTION, - KEY_COMBOBOX, KEY_DND, KEY_EMOJI, KEY_EXIT_BREAK, diff --git a/apps/www/src/config/customizer-plugins.ts b/apps/www/src/config/customizer-plugins.ts index 1ccc57024e..ff1c2d207c 100644 --- a/apps/www/src/config/customizer-plugins.ts +++ b/apps/www/src/config/customizer-plugins.ts @@ -6,7 +6,6 @@ import { KEY_SOFT_BREAK, } from '@udecode/plate-break'; import { KEY_CAPTION } from '@udecode/plate-caption'; -import { KEY_COMBOBOX } from '@udecode/plate-combobox'; import { MARK_COMMENT } from '@udecode/plate-comments'; import { KEY_DND } from '@udecode/plate-dnd'; import { KEY_EMOJI } from '@udecode/plate-emoji'; @@ -129,12 +128,6 @@ export const customizerPlugins = { route: '/docs/column', value: columnValue, }, - combobox: { - id: 'combobox', - label: 'Combobox', - plugins: [KEY_COMBOBOX], - route: '/docs/combobox', - }, comment: { id: 'comment', label: 'Comment', diff --git a/apps/www/src/config/descriptions.ts b/apps/www/src/config/descriptions.ts index 03d34bad91..54bc12f15f 100644 --- a/apps/www/src/config/descriptions.ts +++ b/apps/www/src/config/descriptions.ts @@ -15,7 +15,6 @@ import { KEY_SINGLE_LINE, KEY_SOFT_BREAK, } from '@udecode/plate-break'; -import { KEY_COMBOBOX } from '@udecode/plate-combobox'; import { MARK_COMMENT } from '@udecode/plate-comments'; import { KEY_DND } from '@udecode/plate-dnd'; import { KEY_EMOJI } from '@udecode/plate-emoji'; @@ -66,7 +65,6 @@ export const descriptions: Record = { [KEY_ALIGN]: 'Align your content to different positions.', [KEY_AUTOFORMAT]: 'Apply formatting automatically using shortcodes.', [KEY_BLOCK_SELECTION]: 'Select and manipulate entire text blocks.', - [KEY_COMBOBOX]: 'Select options from a predefined list.', [KEY_DELETE]: 'Remove the current block if empty when pressing delete forward', [KEY_DESERIALIZE_CSV]: 'Copy paste from CSV to Slate.', diff --git a/apps/www/src/lib/plate/demo/plugins/imagePlugins.ts b/apps/www/src/lib/plate/demo/plugins/imagePlugins.ts index 346f21915f..b93dc070b1 100644 --- a/apps/www/src/lib/plate/demo/plugins/imagePlugins.ts +++ b/apps/www/src/lib/plate/demo/plugins/imagePlugins.ts @@ -1,5 +1,4 @@ import { createBasicElementsPlugin } from '@udecode/plate-basic-elements'; -import { createComboboxPlugin } from '@udecode/plate-combobox'; import { createPlugins } from '@udecode/plate-core'; import { createImagePlugin } from '@udecode/plate-media'; import { createSelectOnBackspacePlugin } from '@udecode/plate-select'; @@ -15,7 +14,6 @@ export const imagePlugins = createPlugins( ...basicMarksPlugins, createImagePlugin(), createSelectOnBackspacePlugin(selectOnBackspacePlugin), - createComboboxPlugin(), ], { components: plateUI, diff --git a/apps/www/src/registry/default/example/playground-demo.tsx b/apps/www/src/registry/default/example/playground-demo.tsx index c39bcbe738..bc3648bbd0 100644 --- a/apps/www/src/registry/default/example/playground-demo.tsx +++ b/apps/www/src/registry/default/example/playground-demo.tsx @@ -32,7 +32,6 @@ import { ELEMENT_CODE_BLOCK, createCodeBlockPlugin, } from '@udecode/plate-code-block'; -import { createComboboxPlugin } from '@udecode/plate-combobox'; import { createCommentsPlugin } from '@udecode/plate-comments'; import { Plate, @@ -276,7 +275,6 @@ export const usePlaygroundPlugins = ({ }, }, }), - createComboboxPlugin({ enabled: !!enabled.combobox }), createDndPlugin({ enabled: !!enabled.dnd, options: { enableScroller: true }, diff --git a/apps/www/src/registry/default/plate-ui/combobox.tsx b/apps/www/src/registry/default/plate-ui/combobox.tsx deleted file mode 100644 index ad12816a9e..0000000000 --- a/apps/www/src/registry/default/plate-ui/combobox.tsx +++ /dev/null @@ -1,160 +0,0 @@ -'use client'; - -import React, { useEffect } from 'react'; - -import * as Popover from '@radix-ui/react-popover'; -import { cn, withRef } from '@udecode/cn'; -import { - type ComboboxContentItemProps, - type ComboboxContentProps, - type ComboboxProps, - comboboxActions, - useActiveComboboxStore, - useComboboxContent, - useComboboxContentState, - useComboboxControls, - useComboboxItem, - useComboboxSelectors, -} from '@udecode/plate-combobox'; -import { - useEditorRef, - useEditorSelector, - useEventEditorSelectors, - usePlateSelectors, -} from '@udecode/plate-common'; -import { - createVirtualRef, - getBoundingClientRect, -} from '@udecode/plate-floating'; - -export const ComboboxItem = withRef<'div', ComboboxContentItemProps>( - ({ className, combobox, index, item, onRenderItem, ...rest }, ref) => { - const { props } = useComboboxItem({ combobox, index, item, onRenderItem }); - - return ( -
- ); - } -); - -export function ComboboxContent(props: ComboboxContentProps) { - const { - combobox, - component: Component, - items, - onRenderItem, - portalElement, - } = props; - - const editor = useEditorRef(); - - const filteredItems = useComboboxSelectors.filteredItems(); - const activeComboboxStore = useActiveComboboxStore()!; - - const state = useComboboxContentState({ combobox, items }); - const { menuProps, targetRange } = useComboboxContent(state); - - const virtualRef = createVirtualRef(editor, targetRange ?? undefined, { - fallbackRect: getBoundingClientRect(editor, editor.selection!), - }); - - return ( - - - - - event.preventDefault()} - side="bottom" - sideOffset={5} - > - {Component ? Component({ store: activeComboboxStore }) : null} - - {filteredItems.map((item, index) => ( - - ))} - - - - ); -} - -export function Combobox({ - controlled, - disabled: _disabled, - filter, - id, - maxSuggestions, - onSelectItem, - searchPattern, - sort, - trigger, - ...props -}: ComboboxProps) { - const storeItems = useComboboxSelectors.items(); - const disabled = - _disabled ?? (storeItems.length === 0 && !props.items?.length); - - const focusedEditorId = useEventEditorSelectors.focus?.(); - const combobox = useComboboxControls(); - const activeId = useComboboxSelectors.activeId(); - const selectionDefined = useEditorSelector( - (editor) => !!editor.selection, - [] - ); - const editorId = usePlateSelectors().id(); - - useEffect(() => { - comboboxActions.setComboboxById({ - controlled, - filter, - id, - maxSuggestions, - onSelectItem, - searchPattern, - sort, - trigger, - }); - }, [ - id, - trigger, - searchPattern, - controlled, - onSelectItem, - maxSuggestions, - filter, - sort, - ]); - - if ( - !combobox || - !selectionDefined || - focusedEditorId !== editorId || - activeId !== id || - disabled - ) { - return null; - } - - return ; -} diff --git a/apps/www/src/registry/default/plate-ui/emoji-input-element.tsx b/apps/www/src/registry/default/plate-ui/emoji-input-element.tsx index 4192b3eb8c..a25ca51cdc 100644 --- a/apps/www/src/registry/default/plate-ui/emoji-input-element.tsx +++ b/apps/www/src/registry/default/plate-ui/emoji-input-element.tsx @@ -37,6 +37,7 @@ export const EmojiInputElement = withRef( {...props} > [value, ...keywords].some((keyword) => filterWords(keyword, search)); interface InlineComboboxProps { + element: TElement; children: ReactNode; trigger: string; filter?: FilterFn | false; @@ -70,6 +75,7 @@ interface InlineComboboxProps { } const InlineCombobox = ({ + element, children, filter = defaultFilter, hideWhenNoValue = false, @@ -82,6 +88,13 @@ const InlineCombobox = ({ const inputRef = React.useRef(null); const cursorState = useHTMLInputCursorState(inputRef); + const [pointRef] = useState(() => createPointRef(editor, getStartPoint(editor, findNodePath(editor, element)!))); + + // Unref pointRef on unmount + useEffect(() => () => { + pointRef.unref(); + }, [pointRef]); + const [valueState, setValueState] = useState(''); const hasValueProp = valueProp !== undefined; const value = hasValueProp ? valueProp : valueState; @@ -102,7 +115,7 @@ const InlineCombobox = ({ cursorState, onCancelInput: (cause) => { if (cause !== 'backspace') { - insertText(editor, trigger + value); + insertText(editor, trigger + value, { at: pointRef.current ?? undefined }); } if (cause === 'arrowLeft' || cause === 'arrowRight') { moveSelection(editor, { diff --git a/apps/www/src/registry/default/plate-ui/mention-input-element.tsx b/apps/www/src/registry/default/plate-ui/mention-input-element.tsx index 284de6ae61..124f5dcba8 100644 --- a/apps/www/src/registry/default/plate-ui/mention-input-element.tsx +++ b/apps/www/src/registry/default/plate-ui/mention-input-element.tsx @@ -29,6 +29,7 @@ export const MentionInputElement = withRef( {...props} > ( ref={ref} {...props} > - + diff --git a/apps/www/src/registry/registry.ts b/apps/www/src/registry/registry.ts index 1aaafdc7e4..c6158aac27 100644 --- a/apps/www/src/registry/registry.ts +++ b/apps/www/src/registry/registry.ts @@ -168,17 +168,6 @@ const ui: Registry = [ registryDependencies: [], type: 'components:plate-ui', }, - { - dependencies: [ - '@radix-ui/react-popover', - '@udecode/plate-combobox', - '@udecode/plate-floating', - ], - files: ['plate-ui/combobox.tsx'], - name: 'combobox', - registryDependencies: [], - type: 'components:plate-ui', - }, { dependencies: ['cmdk'], files: ['plate-ui/command.tsx'], From 035752f90b01f07071bc532def495d14dfedee2c Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Mon, 3 Jun 2024 17:47:28 +0100 Subject: [PATCH 39/63] Fix: Trigger is inserted in the wrong place on selection change --- .../src/lib/plate/demo/values/emojiValue.tsx | 4 +- .../default/plate-ui/inline-combobox.tsx | 44 ++++++++++++++----- .../default/plate-ui/slash-input-element.tsx | 2 +- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/apps/www/src/lib/plate/demo/values/emojiValue.tsx b/apps/www/src/lib/plate/demo/values/emojiValue.tsx index 91e1e43e5a..2d4680b9a1 100644 --- a/apps/www/src/lib/plate/demo/values/emojiValue.tsx +++ b/apps/www/src/lib/plate/demo/values/emojiValue.tsx @@ -8,8 +8,6 @@ export const emojiValue: any = ( 🙂 Emoji's Express yourself with a touch of fun 🎉 and emotion 😃. - - Pick from the toolbar or type a colon to open the combobox. - + Pick from the toolbar or type a colon to open the combobox. ); diff --git a/apps/www/src/registry/default/plate-ui/inline-combobox.tsx b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx index a12cfb05b1..29b4641e9a 100644 --- a/apps/www/src/registry/default/plate-ui/inline-combobox.tsx +++ b/apps/www/src/registry/default/plate-ui/inline-combobox.tsx @@ -12,6 +12,8 @@ import React, { useState, } from 'react'; +import type { PointRef } from 'slate'; + import { Combobox, ComboboxItem, @@ -30,10 +32,10 @@ import { useHTMLInputCursorState, } from '@udecode/plate-combobox'; import { - TElement, + type TElement, createPointRef, findNodePath, - getStartPoint, + getPointBefore, insertText, moveSelection, useComposedRef, @@ -64,8 +66,8 @@ export const defaultFilter: FilterFn = ({ keywords = [], value }, search) => [value, ...keywords].some((keyword) => filterWords(keyword, search)); interface InlineComboboxProps { - element: TElement; children: ReactNode; + element: TElement; trigger: string; filter?: FilterFn | false; hideWhenNoValue?: boolean; @@ -75,8 +77,8 @@ interface InlineComboboxProps { } const InlineCombobox = ({ - element, children, + element, filter = defaultFilter, hideWhenNoValue = false, setValue: setValueProp, @@ -88,13 +90,6 @@ const InlineCombobox = ({ const inputRef = React.useRef(null); const cursorState = useHTMLInputCursorState(inputRef); - const [pointRef] = useState(() => createPointRef(editor, getStartPoint(editor, findNodePath(editor, element)!))); - - // Unref pointRef on unmount - useEffect(() => () => { - pointRef.unref(); - }, [pointRef]); - const [valueState, setValueState] = useState(''); const hasValueProp = valueProp !== undefined; const value = hasValueProp ? valueProp : valueState; @@ -110,12 +105,37 @@ const InlineCombobox = ({ [setValueProp, hasValueProp] ); + /** + * Track the point just before the input element so we know where to + * insertText if the combobox closes due to a selection change. + */ + const [insertPoint, setInsertPoint] = useState(null); + + useEffect(() => { + const path = findNodePath(editor, element); + + if (!path) return; + + const point = getPointBefore(editor, path); + + if (!point) return; + + const pointRef = createPointRef(editor, point); + setInsertPoint(pointRef); + + return () => { + pointRef.unref(); + }; + }, [editor, element]); + const { props: inputProps, removeInput } = useComboboxInput({ cancelInputOnBlur: false, cursorState, onCancelInput: (cause) => { if (cause !== 'backspace') { - insertText(editor, trigger + value, { at: pointRef.current ?? undefined }); + insertText(editor, trigger + value, { + at: insertPoint?.current ?? undefined, + }); } if (cause === 'arrowLeft' || cause === 'arrowRight') { moveSelection(editor, { diff --git a/apps/www/src/registry/default/plate-ui/slash-input-element.tsx b/apps/www/src/registry/default/plate-ui/slash-input-element.tsx index b80b4f39b0..819b1a1565 100644 --- a/apps/www/src/registry/default/plate-ui/slash-input-element.tsx +++ b/apps/www/src/registry/default/plate-ui/slash-input-element.tsx @@ -81,7 +81,7 @@ export const SlashInputElement = withRef( ref={ref} {...props} > - + From 928823249b2274ef45092614eaf4b59f8f20d02a Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Wed, 5 Jun 2024 16:10:13 +0100 Subject: [PATCH 40/63] Update registry and docs --- apps/www/content/docs/combobox.mdx | 283 +++-------- ...i-combobox.mdx => emoji-input-element.mdx} | 11 +- .../{combobox.mdx => inline-combobox.mdx} | 27 +- .../docs/components/mention-input-element.mdx | 3 +- apps/www/content/docs/emoji.mdx | 62 +-- apps/www/content/docs/mention.mdx | 139 +----- apps/www/public/registry/index.json | 468 ++++++++++-------- .../styles/default/emoji-input-element.json | 16 + .../styles/default/inline-combobox.json | 15 + .../styles/default/mention-input-element.json | 10 +- .../styles/default/slash-input-element.json | 17 + apps/www/src/__registry__/index.tsx | 30 +- apps/www/src/config/customizer-components.ts | 13 +- apps/www/src/config/customizer-items.ts | 19 +- apps/www/src/config/docs.ts | 3 +- apps/www/src/registry/registry.ts | 30 +- 16 files changed, 491 insertions(+), 655 deletions(-) rename apps/www/content/docs/components/{emoji-combobox.mdx => emoji-input-element.mdx} (75%) rename apps/www/content/docs/components/{combobox.mdx => inline-combobox.mdx} (58%) create mode 100644 apps/www/public/registry/styles/default/emoji-input-element.json create mode 100644 apps/www/public/registry/styles/default/inline-combobox.json create mode 100644 apps/www/public/registry/styles/default/slash-input-element.json diff --git a/apps/www/content/docs/combobox.mdx b/apps/www/content/docs/combobox.mdx index 032f83049c..160542e670 100644 --- a/apps/www/content/docs/combobox.mdx +++ b/apps/www/content/docs/combobox.mdx @@ -1,249 +1,94 @@ --- title: Combobox -description: Select options from a list of predefined values. -docs: - - route: /docs/components/combobox - title: Combobox - - route: /docs/components/emoji-combobox - title: Emoji Combobox - - route: /docs/components/mention-combobox - title: Mention Combobox +description: Utilities for adding comboboxes to your editor. --- - +## TriggerComboboxPlugin -## Features +The TriggerComboboxPlugin mixin configures your plugin to insert a combobox input element when the user types a specified trigger character is typed. -- Displays a combobox for selecting options from a set list. -- Suitable for creating mentions, tags, or slash commands. -- Works in conjunction with the [Mention plugin](/docs/mention). +For example, the [Mention](/docs/mention) plugin uses TriggerComboboxPlugin to insert an `ELEMENT_MENTION_INPUT` whenever the user types `@`. -**Activation Conditions:** +### Usage -- Collapsed text selection. -- Cursor placement immediately after the trigger character. -- Text without spaces follows the trigger character. + -**On Activation:** - -- Sets `id`, `text`, and `targetRange` in the combobox store. - - - -## Installation - -```bash -npm install @udecode/plate-combobox -``` - -## Usage - -```tsx -import { createComboboxPlugin } from '@udecode/plate-combobox'; -import { createMentionPlugin } from '@udecode/plate-mention'; - -const plugins = [ - // ...otherPlugins, - createComboboxPlugin(), - createMentionPlugin(), -]; -``` - -Then render the combobox component inside `Plate`. You can use the [Combobox component](/docs/components/combobox) or create your own. - -## Keyboard Interactions - - - - Highlights the next item in the list. - - - Highlights the previous item in the list. - - Closes the combobox. - - Selects the currently highlighted item. - - - Selects the currently highlighted item. - - - -## API - -### createComboboxPlugin - -### ComboboxProps - -Here are some key aspects of the **`Combobox`**: - -- Multiple Instances: You can render the **`Combobox`** multiple times, each with its unique configuration provided by a different **`ComboboxStateById`**. -- Singleton Behavior: Only one **`Combobox`** can be opened at a time. The state of the active **`Combobox`** is stored in the **`comboboxStore`**. -- Extends **`ComboboxState`**, **`ComboboxStateById`**: - - - -The items for the combobox. An alternative to setting the items is to use -`comboboxActions.items(items)`. - - -A component that is rendered when the combobox is open. Useful for injecting -hooks. - - -A function to render the combobox item. - -- **Default:** item text - - - - -The combobox item. - - - - -The search text. - - - - - - - -The element to which the combobox is rendered. - -- **Default:** `document.body` - - - - - -### ComboboxState - -Represents a combobox's state. The state resides in `comboboxStore`, which uses the [zustood store](https://github.com/udecode/zustood). - - - -Opened combobox ID. - - -A collection of combobox configuration stores, each identified by a unique combobox ID (e.g., one for tags, one for mentions). - -- `ComboboxStateById`: - - - - -Combobox ID. - - -An optional function to filter items by text. - -- **Default:** A function that checks if the item's text begins with the search text. It compares lowercase strings. + +Extend your plugin options type with TriggerComboboxPlugin. ```ts -(search: string) => (item: TComboboxItem) => boolean; +interface MyPlugin extends TriggerComboboxPlugin {} +const createMyPlugin = createPluginFactory({ + // ... +}); + +// Or simply: +const createMyPlugin = createPluginFactory({ + // ... +}); ``` + - - - -An optional function that sorts filtered items before applying `maxSuggestions`. + +Add the withTriggerCombobox override and specify default values for the required options. (See below for the full list of options). ```ts -(search: string) => (a: TComboboxItem, b: TComboboxItem) => - number; +const createMyPlugin = createPluginFactory({ + // ... + withOverrides: withTriggerCombobox, + options: { + createComboboxInput: (trigger) => ({ + children: [{ text: '' }], + trigger, + type: ELEMENT_MY_INPUT, + }), + trigger: '@', + triggerPreviousCharPattern: /^\s?$/, + }, +}); ``` + - - -The maximum number of suggestions to be shown. - -- **Default:** The length of the **`items`** array. - - - - -The trigger character to activate the combobox. - - -An optional regular expression for searching, for example, to allow whitespaces. - - -An optional callback function invoked when an item is selected. + +Define your input element as an inline void element. It's often useful to do this inside a nested plugin. ```ts -(editor: PlateEditor, item: TComboboxItem) => any; +const createMyPlugin = createPluginFactory({ + // ... + plugins: [ + { + isElement: true, + isInline: true, + isVoid: true, + key: ELEMENT_MY_INPUT, + }, + ], +}); ``` - - -Indicates if the opening/closing of the combobox is controlled by the client. - - - - - -The list of unfiltered items. - - -The list of filtered items. - - -The index of the currently highlighted item. - - -The range from the trigger to the cursor. - - -The text that appears after the trigger. - +The input element component can be built using [Inline Combobox](/docs/components/inline-combobox). + - + -### TComboboxItem +### Options -The data structure representing a single item in a combobox. + - - -A unique key for the item. - - -The text of the item. + + A function to create the input node. - -Indicates whether the item is disabled. -- **Default:** `false` + + The character that triggers the combobox. + + + Only trigger the combobox if the char before the trigger character matches a regular expression. For example, `/^\s?$/` matches beginning of the line or a space. - -Data available to `onRenderItem`. + + + A query function to enable the behavior. - - -## API Components - -### useComboboxContent - -A behavior hook for the `ComboboxContent` component. - - - - The items for the combobox. - - - The combobox store. - - - - - - The menu props for the combobox content. - - - The target range of the combobox. - - + + diff --git a/apps/www/content/docs/components/emoji-combobox.mdx b/apps/www/content/docs/components/emoji-input-element.mdx similarity index 75% rename from apps/www/content/docs/components/emoji-combobox.mdx rename to apps/www/content/docs/components/emoji-input-element.mdx index a46401ee53..49a74b1d8c 100644 --- a/apps/www/content/docs/components/emoji-combobox.mdx +++ b/apps/www/content/docs/components/emoji-input-element.mdx @@ -1,10 +1,8 @@ --- -title: Emoji Combobox -description: Enter and select emojis using a combination of text input and a dropdown menu. +title: Emoji Input Element +description: Search for an emoji using an inline combobox. component: true docs: - - route: /docs/combobox - title: Combobox - route: /docs/emoji title: Emoji --- @@ -20,7 +18,7 @@ docs: ```bash -npx @udecode/plate-ui@latest add emoji-combobox +npx @udecode/plate-ui@latest add emoji-input-element ``` @@ -33,6 +31,7 @@ npx @udecode/plate-ui@latest add emoji-combobox Install the following dependencies: +- [Emoji](/docs/emoji) - [Combobox](/docs/combobox) @@ -43,7 +42,7 @@ Copy and paste the following code into your project. - + diff --git a/apps/www/content/docs/components/combobox.mdx b/apps/www/content/docs/components/inline-combobox.mdx similarity index 58% rename from apps/www/content/docs/components/combobox.mdx rename to apps/www/content/docs/components/inline-combobox.mdx index b9033d28f6..10ecc0a78e 100644 --- a/apps/www/content/docs/components/combobox.mdx +++ b/apps/www/content/docs/components/inline-combobox.mdx @@ -1,6 +1,6 @@ --- -title: Combobox -description: Combine a text input field with a dropdown menu for enhanced user interaction. +title: Inline Combobox +description: Enhance inline nodes with accessible comboboxes. component: true docs: - route: /docs/combobox @@ -18,7 +18,7 @@ docs: ```bash -npx @udecode/plate-ui@latest add combobox +npx @udecode/plate-ui@latest add inline-combobox ``` @@ -31,12 +31,18 @@ npx @udecode/plate-ui@latest add combobox Install the following dependencies: +- [Combobox](/docs/combobox) + + + + + +Install the Combobox component from Ariakit: + ```bash -npm install @radix-ui/react-popover @udecode/plate-floating +npm install @ariakit/react ``` -- [Combobox](/docs/combobox) - @@ -45,7 +51,7 @@ Copy and paste the following code into your project. - + @@ -59,9 +65,8 @@ Update the import paths to match your project setup. -## Examples +## Usage - +See [Mention Input](/docs/components/mention-input-element) for an example of how to use Inline Combobox. - - + diff --git a/apps/www/content/docs/components/mention-input-element.mdx b/apps/www/content/docs/components/mention-input-element.mdx index f97f05e807..f2ec269df9 100644 --- a/apps/www/content/docs/components/mention-input-element.mdx +++ b/apps/www/content/docs/components/mention-input-element.mdx @@ -31,7 +31,8 @@ npx @udecode/plate-ui@latest add mention-input-element Install the following dependencies: -- [Mention](/docs/combobox) +- [Mention](/docs/mention) +- [Combobox](/docs/combobox) diff --git a/apps/www/content/docs/emoji.mdx b/apps/www/content/docs/emoji.mdx index f6a1f9d82f..b550f857dc 100644 --- a/apps/www/content/docs/emoji.mdx +++ b/apps/www/content/docs/emoji.mdx @@ -2,8 +2,8 @@ title: Emoji description: Insert emoji inline. docs: - - route: /docs/components/emoji-combobox - title: Emoji Combobox + - route: /docs/components/emoji-input-element + title: Emoji Input Element - route: /docs/components/emoji-dropdown-menu title: Emoji Dropdown Menu - route: /docs/components/emoji-toolbar-dropdown @@ -24,7 +24,7 @@ docs: ## Installation ```bash -npm install @udecode/plate-emoji @udecode/plate-combobox +npm install @udecode/plate-emoji ``` ## Usage @@ -35,7 +35,6 @@ import { createEmojiPlugin } from '@udecode/plate-emoji'; const plugins = [ // ...otherPlugins, - createComboboxPlugin(), createEmojiPlugin(), ]; ``` @@ -44,57 +43,10 @@ const plugins = [ ### createEmojiPlugin - - - -The trigger character(s) for the emoji. - - - -A function to create an Emoji. TData is the type of the data used to create the emoji. - - - -A controller object that controls the emoji triggering behavior. - - - -Indicates whether the emoji trigger is active. - - - -Indicates whether the triggering mark is present. - - - -A function that sets the triggering status. - - - -A function that sets the text. - - - -A function that gets the current text. - - - -A function that checks whether the current triggering mark is enclosing. - - - -A function that gets the size of the current text. - - - -A function that resets the triggering controller. - - - - - - -The ID of the plugin. +Extends [TriggerComboboxPlugin](/docs/combobox#options) + + + A function to specify the node inserted when an emoji is selected. The default behavior is to insert a text node containing the emoji as a Unicode character. diff --git a/apps/www/content/docs/mention.mdx b/apps/www/content/docs/mention.mdx index d7b37c37c3..69ee000dde 100644 --- a/apps/www/content/docs/mention.mdx +++ b/apps/www/content/docs/mention.mdx @@ -4,8 +4,6 @@ description: Enable autocompletion for user mentions. docs: - route: /docs/components/combobox title: Combobox - - route: /docs/components/mention-combobox - title: Mention Combobox - route: /docs/components/mention-element title: Mention Element - route: /docs/components/mention-input-element @@ -26,18 +24,16 @@ docs: ## Installation ```bash -npm install @udecode/plate-mention @udecode/plate-combobox +npm install @udecode/plate-mention ``` ## Usage ```tsx -import { createComboboxPlugin } from '@udecode/plate-combobox'; import { createMentionPlugin } from '@udecode/plate-mention'; const plugins = [ // ...otherPlugins, - createComboboxPlugin(), createMentionPlugin(), ]; ``` @@ -46,51 +42,21 @@ const plugins = [ ### createMentionPlugin - +Extends [TriggerComboboxPlugin](/docs/combobox#options) + - -A function to create the mention node. - - - - A unique ID for the mention plugin. + A function to create the mention node. + Whether to insert a space after the mention. - - The character that triggers the mention (for example, '@' in the case of a - user mention). - - - The pattern that matches the char before the trigger character - (default to `/^\s?$/` that matches beginning of line or space) - - - - -An object containing the key-value pair for creating an input. - - - - The key for the input. - - - The value for the input. - - - - - - A query function to enable the behavior. - - ### getMentionOnSelectItem -Gets the `ComboboxOnSelectItem` handler for selecting an item in the mention combobox. +Gets the handler for selecting an item in the mention combobox. @@ -106,96 +72,7 @@ The plugin key of the mention plugin. - - The `ComboboxOnSelectItem` handler for selecting an item in the mention - combobox. - - - -### findMentionInput - -Finds the mention input node in the editor. - - - -The editor instance. - - - -Additional options for finding the mention input node. - - - - - - - The mention input node entry if found, otherwise `undefined`. - - - -### isNodeMentionInput - -Checks if a node is a mention input node in the editor. - - - - The editor instance. - - - The node to check. - - - - - - `true` if the node is a mention input node, otherwise `false`. - - - -### isSelectionInMentionInput - -Checks if the current selection in the editor is within a mention input. - - - - The editor instance. - - - - - - `true` if the selection is within a mention input, otherwise `false`. + + The handler for selecting an item in the mention combobox. - -### mentionOnKeyDownHandler - -Handles keydown events for mention-related functionality, such as removing mention inputs and moving the selection by offset. - - - - Options for moving the selection by offset. - - - A query function to enable the behavior. - - - - - -### removeMentionInput - -Removes the mention input node at the specified path. - - - - The editor instance. - - - The path of the mention input node to remove. - - diff --git a/apps/www/public/registry/index.json b/apps/www/public/registry/index.json index 0dcc277bb8..a9eab43935 100644 --- a/apps/www/public/registry/index.json +++ b/apps/www/public/registry/index.json @@ -1,19 +1,17 @@ [ { - "name": "editor", "dependencies": [], - "registryDependencies": [], "files": [ "plate-ui/editor.tsx" ], + "name": "editor", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "cloud", "dependencies": [ "@udecode/plate-cloud" ], - "registryDependencies": [], "files": [ "plate-ui/cloud.tsx", "plate-ui/cloud-attachment-element.tsx", @@ -22,48 +20,42 @@ "plate-ui/cloud-status-bar.tsx", "plate-ui/cloud-toolbar-buttons.tsx" ], + "name": "cloud", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "code-block-element", "dependencies": [ "@udecode/plate-code-block" ], - "registryDependencies": [ - "command" - ], "files": [ "plate-ui/code-block-element.tsx", "plate-ui/code-block-element.css", "plate-ui/code-block-combobox.tsx" ], + "name": "code-block-element", + "registryDependencies": [ + "command" + ], "type": "components:plate-ui" }, { - "name": "column-element", "dependencies": [ "@udecode/plate-layout" ], - "registryDependencies": [ - "command", - "resizable" - ], "files": [ "plate-ui/column-element.tsx", "plate-ui/column-group-element.tsx" ], + "name": "column-element", + "registryDependencies": [ + "command", + "resizable" + ], "type": "components:plate-ui" }, { - "name": "color-dropdown-menu", "dependencies": [], - "registryDependencies": [ - "dropdown-menu", - "toolbar", - "separator", - "button", - "tooltip" - ], "files": [ "plate-ui/color-dropdown-menu.tsx", "plate-ui/color-constants.ts", @@ -72,17 +64,20 @@ "plate-ui/color-picker.tsx", "plate-ui/colors-custom.tsx" ], + "name": "color-dropdown-menu", + "registryDependencies": [ + "dropdown-menu", + "toolbar", + "separator", + "button", + "tooltip" + ], "type": "components:plate-ui" }, { - "name": "comments-popover", "dependencies": [ "@udecode/plate-comments" ], - "registryDependencies": [ - "popover", - "avatar" - ], "files": [ "plate-ui/comments-popover.tsx", "plate-ui/comment-avatar.tsx", @@ -93,32 +88,33 @@ "plate-ui/comment-resolve-button.tsx", "plate-ui/comment-value.tsx" ], + "name": "comments-popover", + "registryDependencies": [ + "popover", + "avatar" + ], "type": "components:plate-ui" }, { - "name": "draggable", "dependencies": [ "@udecode/plate-dnd", "react-dnd", "react-dnd-html5-backend" ], - "registryDependencies": [ - "tooltip" - ], "files": [ "plate-ui/draggable.tsx", "plate-ui/with-draggables.tsx" ], + "name": "draggable", + "registryDependencies": [ + "tooltip" + ], "type": "components:plate-ui" }, { - "name": "emoji-dropdown-menu", "dependencies": [ "@radix-ui/react-popover" ], - "registryDependencies": [ - "toolbar" - ], "files": [ "plate-ui/emoji-dropdown-menu.tsx", "plate-ui/emoji-toolbar-dropdown.tsx", @@ -130,201 +126,221 @@ "plate-ui/emoji-picker-search-and-clear.tsx", "plate-ui/emoji-picker-search-bar.tsx" ], + "name": "emoji-dropdown-menu", + "registryDependencies": [ + "toolbar" + ], "type": "components:plate-ui" }, { - "name": "align-dropdown-menu", "dependencies": [ - "@udecode/plate-alignment" + "@udecode/plate-emoji" + ], + "files": [ + "plate-ui/emoji-input-element.tsx" ], + "name": "emoji-input-element", "registryDependencies": [ - "dropdown-menu", - "toolbar" + "inline-combobox" + ], + "type": "components:plate-ui" + }, + { + "dependencies": [ + "@udecode/plate-alignment" ], "files": [ "plate-ui/align-dropdown-menu.tsx" ], + "name": "align-dropdown-menu", + "registryDependencies": [ + "dropdown-menu", + "toolbar" + ], "type": "components:plate-ui" }, { - "name": "avatar", "dependencies": [ "@radix-ui/react-avatar" ], - "registryDependencies": [], "files": [ "plate-ui/avatar.tsx" ], + "name": "avatar", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "blockquote-element", "dependencies": [ "@udecode/plate-block-quote" ], - "registryDependencies": [], "files": [ "plate-ui/blockquote-element.tsx" ], + "name": "blockquote-element", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "button", "dependencies": [ "@radix-ui/react-slot" ], - "registryDependencies": [], "files": [ "plate-ui/button.tsx" ], + "name": "button", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "caption", "dependencies": [ "@udecode/plate-caption" ], - "registryDependencies": [], "files": [ "plate-ui/caption.tsx" ], + "name": "caption", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "checkbox", "dependencies": [ "@radix-ui/react-checkbox" ], - "registryDependencies": [], "files": [ "plate-ui/checkbox.tsx" ], + "name": "checkbox", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "code-leaf", "dependencies": [ "@udecode/plate-basic-marks" ], - "registryDependencies": [], "files": [ "plate-ui/code-leaf.tsx" ], + "name": "code-leaf", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "code-line-element", "dependencies": [ "@udecode/plate-code-block" ], - "registryDependencies": [], "files": [ "plate-ui/code-line-element.tsx" ], + "name": "code-line-element", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "code-syntax-leaf", "dependencies": [ "@udecode/plate-code-block" ], - "registryDependencies": [], "files": [ "plate-ui/code-syntax-leaf.tsx" ], + "name": "code-syntax-leaf", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "command", "dependencies": [ "cmdk" ], - "registryDependencies": [ - "dialog" - ], "files": [ "plate-ui/command.tsx" ], + "name": "command", + "registryDependencies": [ + "dialog" + ], "type": "components:plate-ui" }, { - "name": "comment-leaf", "dependencies": [ "@udecode/plate-comments" ], - "registryDependencies": [], "files": [ "plate-ui/comment-leaf.tsx" ], + "name": "comment-leaf", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "comment-toolbar-button", "dependencies": [], - "registryDependencies": [], "files": [ "plate-ui/comment-toolbar-button.tsx" ], + "name": "comment-toolbar-button", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "cursor-overlay", "dependencies": [], - "registryDependencies": [], "files": [ "plate-ui/cursor-overlay.tsx" ], + "name": "cursor-overlay", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "dialog", "dependencies": [ "@radix-ui/react-dialog" ], - "registryDependencies": [], "files": [ "plate-ui/dialog.tsx" ], + "name": "dialog", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "dropdown-menu", "dependencies": [ "@radix-ui/react-dropdown-menu" ], - "registryDependencies": [], "files": [ "plate-ui/dropdown-menu.tsx" ], + "name": "dropdown-menu", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "excalidraw-element", "dependencies": [ "@udecode/plate-excalidraw" ], - "registryDependencies": [], "files": [ "plate-ui/excalidraw-element.tsx" ], + "name": "excalidraw-element", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "fixed-toolbar", "dependencies": [], - "registryDependencies": [ - "toolbar" - ], "files": [ "plate-ui/fixed-toolbar.tsx" ], + "name": "fixed-toolbar", + "registryDependencies": [ + "toolbar" + ], "type": "components:plate-ui" }, { - "name": "fixed-toolbar-buttons", "dependencies": [ "@udecode/plate-basic-marks" ], + "files": [ + "plate-ui/fixed-toolbar-buttons.tsx" + ], + "name": "fixed-toolbar-buttons", "registryDependencies": [ "toolbar", "insert-dropdown-menu", @@ -332,542 +348,578 @@ "mode-dropdown-menu", "turn-into-dropdown-menu" ], - "files": [ - "plate-ui/fixed-toolbar-buttons.tsx" - ], "type": "components:plate-ui" }, { - "name": "floating-toolbar", "dependencies": [ "@udecode/plate-floating" ], - "registryDependencies": [ - "toolbar" - ], "files": [ "plate-ui/floating-toolbar.tsx" ], + "name": "floating-toolbar", + "registryDependencies": [ + "toolbar" + ], "type": "components:plate-ui" }, { - "name": "floating-toolbar-buttons", "dependencies": [ "@udecode/plate-basic-marks" ], + "files": [ + "plate-ui/floating-toolbar-buttons.tsx" + ], + "name": "floating-toolbar-buttons", "registryDependencies": [ "mark-toolbar-button", "more-dropdown-menu", "turn-into-dropdown-menu" ], - "files": [ - "plate-ui/floating-toolbar-buttons.tsx" - ], "type": "components:plate-ui" }, { - "name": "heading-element", "dependencies": [ "@udecode/plate-heading" ], - "registryDependencies": [], "files": [ "plate-ui/heading-element.tsx" ], + "name": "heading-element", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "highlight-leaf", "dependencies": [ "@udecode/plate-highlight" ], - "registryDependencies": [], "files": [ "plate-ui/highlight-leaf.tsx" ], + "name": "highlight-leaf", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "hr-element", "dependencies": [ "@udecode/plate-horizontal-rule" ], - "registryDependencies": [], "files": [ "plate-ui/hr-element.tsx" ], + "name": "hr-element", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "image-element", "dependencies": [ "@udecode/plate-media" ], + "files": [ + "plate-ui/image-element.tsx" + ], + "name": "image-element", "registryDependencies": [ "media-popover", "caption", "resizable" ], - "files": [ - "plate-ui/image-element.tsx" - ], "type": "components:plate-ui" }, { - "name": "indent-list-toolbar-button", "dependencies": [ "@udecode/plate-indent-list" ], - "registryDependencies": [ - "toolbar" - ], "files": [ "plate-ui/indent-list-toolbar-button.tsx" ], + "name": "indent-list-toolbar-button", + "registryDependencies": [ + "toolbar" + ], "type": "components:plate-ui" }, { - "name": "indent-toolbar-button", "dependencies": [ "@udecode/plate-indent" ], + "files": [ + "plate-ui/indent-toolbar-button.tsx" + ], + "name": "indent-toolbar-button", "registryDependencies": [ "toolbar" ], + "type": "components:plate-ui" + }, + { + "dependencies": [ + "@ariakit/react", + "@udecode/plate-combobox" + ], "files": [ - "plate-ui/indent-toolbar-button.tsx" + "plate-ui/inline-combobox.tsx" ], + "name": "inline-combobox", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "input", "dependencies": [], - "registryDependencies": [], "files": [ "plate-ui/input.tsx" ], + "name": "input", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "insert-dropdown-menu", "dependencies": [ "@udecode/plate-block-quote", "@udecode/plate-heading", "@udecode/plate-paragraph" ], + "files": [ + "plate-ui/insert-dropdown-menu.tsx" + ], + "name": "insert-dropdown-menu", "registryDependencies": [ "dropdown-menu", "toolbar" ], - "files": [ - "plate-ui/insert-dropdown-menu.tsx" - ], "type": "components:plate-ui" }, { - "name": "kbd-leaf", "dependencies": [ "@udecode/plate-kbd" ], - "registryDependencies": [], "files": [ "plate-ui/kbd-leaf.tsx" ], + "name": "kbd-leaf", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "line-height-dropdown-menu", "dependencies": [ "@udecode/plate-line-height" ], + "files": [ + "plate-ui/line-height-dropdown-menu.tsx" + ], + "name": "line-height-dropdown-menu", "registryDependencies": [ "toolbar", "dropdown-menu" ], - "files": [ - "plate-ui/line-height-dropdown-menu.tsx" - ], "type": "components:plate-ui" }, { - "name": "link-element", "dependencies": [ "@udecode/plate-link" ], - "registryDependencies": [], "files": [ "plate-ui/link-element.tsx" ], + "name": "link-element", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "link-floating-toolbar", "dependencies": [ "@udecode/plate-link" ], + "files": [ + "plate-ui/link-floating-toolbar.tsx" + ], + "name": "link-floating-toolbar", "registryDependencies": [ "button", "input", "popover", "separator" ], - "files": [ - "plate-ui/link-floating-toolbar.tsx" - ], "type": "components:plate-ui" }, { - "name": "link-toolbar-button", "dependencies": [ "@udecode/plate-link" ], - "registryDependencies": [ - "toolbar" - ], "files": [ "plate-ui/link-toolbar-button.tsx" ], + "name": "link-toolbar-button", + "registryDependencies": [ + "toolbar" + ], "type": "components:plate-ui" }, { - "name": "list-element", "dependencies": [ "@udecode/plate-list" ], - "registryDependencies": [], "files": [ "plate-ui/list-element.tsx" ], + "name": "list-element", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "list-toolbar-button", "dependencies": [ "@udecode/plate-list" ], - "registryDependencies": [ - "toolbar" - ], "files": [ "plate-ui/list-toolbar-button.tsx" ], + "name": "list-toolbar-button", + "registryDependencies": [ + "toolbar" + ], "type": "components:plate-ui" }, { - "name": "mark-toolbar-button", "dependencies": [ "@udecode/plate-basic-marks" ], - "registryDependencies": [ - "toolbar" - ], "files": [ "plate-ui/mark-toolbar-button.tsx" ], + "name": "mark-toolbar-button", + "registryDependencies": [ + "toolbar" + ], "type": "components:plate-ui" }, { - "name": "media-embed-element", "dependencies": [ "@udecode/plate-media", "react-tweet", "react-lite-youtube-embed" ], + "files": [ + "plate-ui/media-embed-element.tsx" + ], + "name": "media-embed-element", "registryDependencies": [ "media-popover", "caption", "resizable" ], - "files": [ - "plate-ui/media-embed-element.tsx" - ], "type": "components:plate-ui" }, { - "name": "media-popover", "dependencies": [ "@udecode/plate-media" ], + "files": [ + "plate-ui/media-popover.tsx" + ], + "name": "media-popover", "registryDependencies": [ "button", "input", "popover", "separator" ], - "files": [ - "plate-ui/media-popover.tsx" - ], "type": "components:plate-ui" }, { - "name": "media-toolbar-button", "dependencies": [ "@udecode/plate-media" ], + "files": [ + "plate-ui/media-toolbar-button.tsx" + ], + "name": "media-toolbar-button", "registryDependencies": [ "toolbar" ], + "type": "components:plate-ui" + }, + { + "dependencies": [ + "@udecode/plate-mention" + ], "files": [ - "plate-ui/media-toolbar-button.tsx" + "plate-ui/mention-element.tsx" ], + "name": "mention-element", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "mention-element", "dependencies": [ "@udecode/plate-mention" ], - "registryDependencies": [], "files": [ "plate-ui/mention-element.tsx" ], + "name": "mention-element", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "mention-input-element", "dependencies": [ "@udecode/plate-mention" ], - "registryDependencies": [], "files": [ "plate-ui/mention-input-element.tsx" ], + "name": "mention-input-element", + "registryDependencies": [ + "inline-combobox" + ], "type": "components:plate-ui" }, { - "name": "mode-dropdown-menu", "dependencies": [], + "files": [ + "plate-ui/mode-dropdown-menu.tsx" + ], + "name": "mode-dropdown-menu", "registryDependencies": [ "dropdown-menu", "toolbar" ], - "files": [ - "plate-ui/mode-dropdown-menu.tsx" - ], "type": "components:plate-ui" }, { - "name": "more-dropdown-menu", "dependencies": [ "@udecode/plate-basic-marks" ], + "files": [ + "plate-ui/more-dropdown-menu.tsx" + ], + "name": "more-dropdown-menu", "registryDependencies": [ "dropdown-menu", "toolbar" ], - "files": [ - "plate-ui/more-dropdown-menu.tsx" - ], "type": "components:plate-ui" }, { - "name": "outdent-toolbar-button", "dependencies": [ "@udecode/plate-indent" ], - "registryDependencies": [ - "toolbar" - ], "files": [ "plate-ui/outdent-toolbar-button.tsx" ], + "name": "outdent-toolbar-button", + "registryDependencies": [ + "toolbar" + ], "type": "components:plate-ui" }, { - "name": "paragraph-element", "dependencies": [ "@udecode/plate-paragraph" ], - "registryDependencies": [], "files": [ "plate-ui/paragraph-element.tsx" ], + "name": "paragraph-element", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "placeholder", "dependencies": [ "@udecode/plate-heading" ], - "registryDependencies": [ - "paragraph-element" - ], "files": [ "plate-ui/placeholder.tsx" ], + "name": "placeholder", + "registryDependencies": [ + "paragraph-element" + ], "type": "components:plate-ui" }, { - "name": "popover", "dependencies": [ "@radix-ui/react-popover" ], - "registryDependencies": [], "files": [ "plate-ui/popover.tsx" ], + "name": "popover", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "search-highlight-leaf", "dependencies": [ "@udecode/plate-find-replace" ], - "registryDependencies": [], "files": [ "plate-ui/search-highlight-leaf.tsx" ], + "name": "search-highlight-leaf", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "separator", "dependencies": [ "@radix-ui/react-separator" ], - "registryDependencies": [], "files": [ "plate-ui/separator.tsx" ], + "name": "separator", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "table-cell-element", "dependencies": [ - "@udecode/plate-table" + "@udecode/plate-heading", + "@udecode/plate-indent-list" ], + "files": [ + "plate-ui/slash-input-element.tsx" + ], + "name": "slash-input-element", "registryDependencies": [ - "resizable" + "inline-combobox" + ], + "type": "components:plate-ui" + }, + { + "dependencies": [ + "@udecode/plate-table" ], "files": [ "plate-ui/table-cell-element.tsx" ], + "name": "table-cell-element", + "registryDependencies": [ + "resizable" + ], "type": "components:plate-ui" }, { - "name": "table-dropdown-menu", "dependencies": [ "@udecode/plate-table" ], + "files": [ + "plate-ui/table-dropdown-menu.tsx" + ], + "name": "table-dropdown-menu", "registryDependencies": [ "dropdown-menu", "toolbar" ], - "files": [ - "plate-ui/table-dropdown-menu.tsx" - ], "type": "components:plate-ui" }, { - "name": "table-element", "dependencies": [ "@udecode/plate-table" ], - "registryDependencies": [ - "dropdown-menu" - ], "files": [ "plate-ui/table-element.tsx" ], + "name": "table-element", + "registryDependencies": [ + "dropdown-menu" + ], "type": "components:plate-ui" }, { - "name": "table-row-element", "dependencies": [ "@udecode/plate-table" ], - "registryDependencies": [], "files": [ "plate-ui/table-row-element.tsx" ], + "name": "table-row-element", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "todo-list-element", "dependencies": [ "@udecode/plate-list" ], - "registryDependencies": [ - "checkbox" - ], "files": [ "plate-ui/todo-list-element.tsx" ], + "name": "todo-list-element", + "registryDependencies": [ + "checkbox" + ], "type": "components:plate-ui" }, { - "name": "toggle-element", "dependencies": [ "@udecode/plate-toggle" ], - "registryDependencies": [], "files": [ "plate-ui/toggle-element.tsx" ], + "name": "toggle-element", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "toggle-toolbar-button", "dependencies": [ "@udecode/plate-toggle" ], - "registryDependencies": [ - "toolbar" - ], "files": [ "plate-ui/toggle-toolbar-button.tsx" ], + "name": "toggle-toolbar-button", + "registryDependencies": [ + "toolbar" + ], "type": "components:plate-ui" }, { - "name": "toolbar", "dependencies": [ "@radix-ui/react-toolbar" ], + "files": [ + "plate-ui/toolbar.tsx" + ], + "name": "toolbar", "registryDependencies": [ "tooltip", "separator" ], - "files": [ - "plate-ui/toolbar.tsx" - ], "type": "components:plate-ui" }, { - "name": "tooltip", "dependencies": [ "@radix-ui/react-tooltip" ], - "registryDependencies": [], "files": [ "plate-ui/tooltip.tsx" ], + "name": "tooltip", + "registryDependencies": [], "type": "components:plate-ui" }, { - "name": "turn-into-dropdown-menu", "dependencies": [ "@udecode/plate-block-quote", "@udecode/plate-heading", "@udecode/plate-paragraph" ], + "files": [ + "plate-ui/turn-into-dropdown-menu.tsx" + ], + "name": "turn-into-dropdown-menu", "registryDependencies": [ "dropdown-menu", "toolbar" ], - "files": [ - "plate-ui/turn-into-dropdown-menu.tsx" - ], "type": "components:plate-ui" }, { - "name": "resizable", "dependencies": [ "@udecode/plate-resizable" ], - "registryDependencies": [], "files": [ "plate-ui/resizable.tsx" ], + "name": "resizable", + "registryDependencies": [], "type": "components:plate-ui" } -] +] \ No newline at end of file diff --git a/apps/www/public/registry/styles/default/emoji-input-element.json b/apps/www/public/registry/styles/default/emoji-input-element.json new file mode 100644 index 0000000000..094da67fcf --- /dev/null +++ b/apps/www/public/registry/styles/default/emoji-input-element.json @@ -0,0 +1,16 @@ +{ + "dependencies": [ + "@udecode/plate-emoji" + ], + "files": [ + { + "content": "import React, { useMemo, useState } from 'react';\n\nimport { withRef } from '@udecode/cn';\nimport { PlateElement } from '@udecode/plate-common';\nimport { EmojiInlineIndexSearch, insertEmoji } from '@udecode/plate-emoji';\n\nimport { useDebounce } from '@/hooks/use-debounce';\n\nimport {\n InlineCombobox,\n InlineComboboxContent,\n InlineComboboxEmpty,\n InlineComboboxInput,\n InlineComboboxItem,\n} from './inline-combobox';\n\nexport const EmojiInputElement = withRef(\n ({ className, ...props }, ref) => {\n const { children, editor, element } = props;\n const [value, setValue] = useState('');\n const debouncedValue = useDebounce(value, 100);\n const isPending = value !== debouncedValue;\n\n const filteredEmojis = useMemo(() => {\n if (debouncedValue.trim().length === 0) return [];\n\n return EmojiInlineIndexSearch.getInstance()\n .search(debouncedValue.replace(/:$/, ''))\n .get();\n }, [debouncedValue]);\n\n return (\n \n \n \n\n \n {!isPending && (\n No matching emoji found\n )}\n\n {filteredEmojis.map((emoji) => (\n insertEmoji(editor, emoji)}\n value={emoji.name}\n >\n {emoji.skins[0].native} {emoji.name}\n \n ))}\n \n \n\n {children}\n \n );\n }\n);\n", + "name": "emoji-input-element.tsx" + } + ], + "name": "emoji-input-element", + "registryDependencies": [ + "inline-combobox" + ], + "type": "components:plate-ui" +} \ No newline at end of file diff --git a/apps/www/public/registry/styles/default/inline-combobox.json b/apps/www/public/registry/styles/default/inline-combobox.json new file mode 100644 index 0000000000..540153d93b --- /dev/null +++ b/apps/www/public/registry/styles/default/inline-combobox.json @@ -0,0 +1,15 @@ +{ + "dependencies": [ + "@ariakit/react", + "@udecode/plate-combobox" + ], + "files": [ + { + "content": "import React, {\n type HTMLAttributes,\n type ReactNode,\n type RefObject,\n createContext,\n forwardRef,\n startTransition,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n} from 'react';\n\nimport type { PointRef } from 'slate';\n\nimport {\n Combobox,\n ComboboxItem,\n type ComboboxItemProps,\n ComboboxPopover,\n ComboboxProvider,\n Portal,\n useComboboxContext,\n useComboboxStore,\n} from '@ariakit/react';\nimport { cn } from '@udecode/cn';\nimport {\n type UseComboboxInputResult,\n filterWords,\n useComboboxInput,\n useHTMLInputCursorState,\n} from '@udecode/plate-combobox';\nimport {\n type TElement,\n createPointRef,\n findNodePath,\n getPointBefore,\n insertText,\n moveSelection,\n useComposedRef,\n useEditorRef,\n} from '@udecode/plate-common';\nimport { cva } from 'class-variance-authority';\n\ntype FilterFn = (\n item: { keywords?: string[]; value: string },\n search: string\n) => boolean;\n\ninterface InlineComboboxContextValue {\n filter: FilterFn | false;\n inputProps: UseComboboxInputResult['props'];\n inputRef: RefObject;\n removeInput: UseComboboxInputResult['removeInput'];\n setHasEmpty: (hasEmpty: boolean) => void;\n showTrigger: boolean;\n trigger: string;\n}\n\nconst InlineComboboxContext = createContext(\n null as any\n);\n\nexport const defaultFilter: FilterFn = ({ keywords = [], value }, search) =>\n [value, ...keywords].some((keyword) => filterWords(keyword, search));\n\ninterface InlineComboboxProps {\n children: ReactNode;\n element: TElement;\n trigger: string;\n filter?: FilterFn | false;\n hideWhenNoValue?: boolean;\n setValue?: (value: string) => void;\n showTrigger?: boolean;\n value?: string;\n}\n\nconst InlineCombobox = ({\n children,\n element,\n filter = defaultFilter,\n hideWhenNoValue = false,\n setValue: setValueProp,\n showTrigger = true,\n trigger,\n value: valueProp,\n}: InlineComboboxProps) => {\n const editor = useEditorRef();\n const inputRef = React.useRef(null);\n const cursorState = useHTMLInputCursorState(inputRef);\n\n const [valueState, setValueState] = useState('');\n const hasValueProp = valueProp !== undefined;\n const value = hasValueProp ? valueProp : valueState;\n\n const setValue = useCallback(\n (newValue: string) => {\n setValueProp?.(newValue);\n\n if (!hasValueProp) {\n setValueState(newValue);\n }\n },\n [setValueProp, hasValueProp]\n );\n\n /**\n * Track the point just before the input element so we know where to\n * insertText if the combobox closes due to a selection change.\n */\n const [insertPoint, setInsertPoint] = useState(null);\n\n useEffect(() => {\n const path = findNodePath(editor, element);\n\n if (!path) return;\n\n const point = getPointBefore(editor, path);\n\n if (!point) return;\n\n const pointRef = createPointRef(editor, point);\n setInsertPoint(pointRef);\n\n return () => {\n pointRef.unref();\n };\n }, [editor, element]);\n\n const { props: inputProps, removeInput } = useComboboxInput({\n cancelInputOnBlur: false,\n cursorState,\n onCancelInput: (cause) => {\n if (cause !== 'backspace') {\n insertText(editor, trigger + value, {\n at: insertPoint?.current ?? undefined,\n });\n }\n if (cause === 'arrowLeft' || cause === 'arrowRight') {\n moveSelection(editor, {\n distance: 1,\n reverse: cause === 'arrowLeft',\n });\n }\n },\n ref: inputRef,\n });\n\n const [hasEmpty, setHasEmpty] = useState(false);\n\n const contextValue: InlineComboboxContextValue = useMemo(\n () => ({\n filter,\n inputProps,\n inputRef,\n removeInput,\n setHasEmpty,\n showTrigger,\n trigger,\n }),\n [\n trigger,\n showTrigger,\n filter,\n inputRef,\n inputProps,\n removeInput,\n setHasEmpty,\n ]\n );\n\n const store = useComboboxStore({\n // open: ,\n setValue: (newValue) => startTransition(() => setValue(newValue)),\n });\n\n const items = store.useState('items');\n\n useEffect;\n\n /**\n * If there is no active ID and the list of items changes, select the first\n * item.\n */\n useEffect(() => {\n if (!store.getState().activeId) {\n store.setActiveId(store.first());\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [items, store]);\n\n return (\n \n 0 || hasEmpty) &&\n (!hideWhenNoValue || value.length > 0)\n }\n store={store}\n >\n \n {children}\n \n \n \n );\n};\n\nconst InlineComboboxInput = forwardRef<\n HTMLInputElement,\n HTMLAttributes\n>(({ className, ...props }, propRef) => {\n const {\n inputProps,\n inputRef: contextRef,\n showTrigger,\n trigger,\n } = useContext(InlineComboboxContext);\n\n const store = useComboboxContext()!;\n const value = store.useState('value');\n\n const ref = useComposedRef(propRef, contextRef);\n\n /**\n * To create an auto-resizing input, we render a visually hidden span\n * containing the input value and position the input element on top of it.\n * This works well for all cases except when input exceeds the width of the\n * container.\n */\n\n return (\n <>\n {showTrigger && trigger}\n\n \n \n {value || '\\u200B'}\n \n\n \n \n \n );\n});\n\nInlineComboboxInput.displayName = 'InlineComboboxInput';\n\nconst InlineComboboxContent: typeof ComboboxPopover = ({\n className,\n ...props\n}) => {\n // Portal prevents CSS from leaking into popover\n return (\n \n \n \n );\n};\n\nconst comboboxItemVariants = cva(\n 'relative flex h-9 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none',\n {\n defaultVariants: {\n interactive: true,\n },\n variants: {\n interactive: {\n false: '',\n true: 'cursor-pointer transition-colors hover:bg-accent hover:text-accent-foreground data-[active-item=true]:bg-accent data-[active-item=true]:text-accent-foreground',\n },\n },\n }\n);\n\nexport type InlineComboboxItemProps = {\n keywords?: string[];\n} & ComboboxItemProps &\n Required>;\n\nconst InlineComboboxItem = ({\n className,\n keywords,\n onClick,\n ...props\n}: InlineComboboxItemProps) => {\n const { value } = props;\n\n const { filter, removeInput } = useContext(InlineComboboxContext);\n\n const store = useComboboxContext()!;\n\n // Optimization: Do not subscribe to value if filter is false\n const search = filter && store.useState('value');\n\n const visible = useMemo(\n () => !filter || filter({ keywords, value }, search as string),\n [filter, value, keywords, search]\n );\n\n if (!visible) return null;\n\n return (\n {\n removeInput(true);\n onClick?.(event);\n }}\n {...props}\n />\n );\n};\n\nconst InlineComboboxEmpty = ({\n children,\n className,\n}: HTMLAttributes) => {\n const { setHasEmpty } = useContext(InlineComboboxContext);\n const store = useComboboxContext()!;\n const items = store.useState('items');\n\n useEffect(() => {\n setHasEmpty(true);\n\n return () => {\n setHasEmpty(false);\n };\n }, [setHasEmpty]);\n\n if (items.length > 0) return null;\n\n return (\n \n {children}\n
\n );\n};\n\nexport {\n InlineCombobox,\n InlineComboboxContent,\n InlineComboboxEmpty,\n InlineComboboxInput,\n InlineComboboxItem,\n};\n", + "name": "inline-combobox.tsx" + } + ], + "name": "inline-combobox", + "registryDependencies": [], + "type": "components:plate-ui" +} \ No newline at end of file diff --git a/apps/www/public/registry/styles/default/mention-input-element.json b/apps/www/public/registry/styles/default/mention-input-element.json index aadfafb02c..bf61f03b2e 100644 --- a/apps/www/public/registry/styles/default/mention-input-element.json +++ b/apps/www/public/registry/styles/default/mention-input-element.json @@ -1,14 +1,16 @@ { - "name": "mention-input-element", "dependencies": [ "@udecode/plate-mention" ], - "registryDependencies": [], "files": [ { - "name": "mention-input-element.tsx", - "content": "import React, { useState } from 'react';\nimport { cn, withRef } from '@udecode/cn';\nimport { PlateElement } from '@udecode/plate-common';\nimport { getMentionOnSelectItem } from '@udecode/plate-mention';\n\nimport { MENTIONABLES } from '@/lib/plate/demo/values/mentionables';\n\nimport {\n InlineCombobox,\n InlineComboboxContent,\n InlineComboboxEmpty,\n InlineComboboxInput,\n InlineComboboxItem,\n} from './inline-combobox';\n\nconst onSelectItem = getMentionOnSelectItem();\n\nexport const MentionInputElement = withRef(\n ({ className, ...props }, ref) => {\n const { children, element, editor } = props;\n const [search, setSearch] = useState('');\n\n return (\n \n \n \n \n \n\n \n No results found\n\n {MENTIONABLES.map((item) => (\n onSelectItem(editor, item, search)}\n >\n {item.text}\n \n ))}\n \n \n\n {children}\n \n );\n }\n);\n" + "content": "import React, { useState } from 'react';\n\nimport { cn, withRef } from '@udecode/cn';\nimport { PlateElement } from '@udecode/plate-common';\nimport { getMentionOnSelectItem } from '@udecode/plate-mention';\n\nimport { MENTIONABLES } from '@/lib/plate/demo/values/mentionables';\n\nimport {\n InlineCombobox,\n InlineComboboxContent,\n InlineComboboxEmpty,\n InlineComboboxInput,\n InlineComboboxItem,\n} from './inline-combobox';\n\nconst onSelectItem = getMentionOnSelectItem();\n\nexport const MentionInputElement = withRef(\n ({ className, ...props }, ref) => {\n const { children, editor, element } = props;\n const [search, setSearch] = useState('');\n\n return (\n \n \n \n \n \n\n \n No results found\n\n {MENTIONABLES.map((item) => (\n onSelectItem(editor, item, search)}\n value={item.text}\n >\n {item.text}\n \n ))}\n \n \n\n {children}\n \n );\n }\n);\n", + "name": "mention-input-element.tsx" } ], + "name": "mention-input-element", + "registryDependencies": [ + "inline-combobox" + ], "type": "components:plate-ui" } \ No newline at end of file diff --git a/apps/www/public/registry/styles/default/slash-input-element.json b/apps/www/public/registry/styles/default/slash-input-element.json new file mode 100644 index 0000000000..46dfefb9b1 --- /dev/null +++ b/apps/www/public/registry/styles/default/slash-input-element.json @@ -0,0 +1,17 @@ +{ + "dependencies": [ + "@udecode/plate-heading", + "@udecode/plate-indent-list" + ], + "files": [ + { + "content": "import React, { type ComponentType, type SVGProps } from 'react';\n\nimport { withRef } from '@udecode/cn';\nimport {\n type PlateEditor,\n PlateElement,\n toggleNodeType,\n} from '@udecode/plate-common';\nimport { ELEMENT_H1, ELEMENT_H2, ELEMENT_H3 } from '@udecode/plate-heading';\nimport { ListStyleType, toggleIndentList } from '@udecode/plate-indent-list';\n\nimport { Icons } from '@/components/icons';\n\nimport {\n InlineCombobox,\n InlineComboboxContent,\n InlineComboboxEmpty,\n InlineComboboxInput,\n InlineComboboxItem,\n} from './inline-combobox';\n\ninterface SlashCommandRule {\n icon: ComponentType>;\n onSelect: (editor: PlateEditor) => void;\n value: string;\n keywords?: string[];\n}\n\nconst rules: SlashCommandRule[] = [\n {\n icon: Icons.h1,\n onSelect: (editor) => {\n toggleNodeType(editor, { activeType: ELEMENT_H1 });\n },\n value: 'Heading 1',\n },\n {\n icon: Icons.h2,\n onSelect: (editor) => {\n toggleNodeType(editor, { activeType: ELEMENT_H2 });\n },\n value: 'Heading 2',\n },\n {\n icon: Icons.h3,\n onSelect: (editor) => {\n toggleNodeType(editor, { activeType: ELEMENT_H3 });\n },\n value: 'Heading 3',\n },\n {\n icon: Icons.ul,\n keywords: ['ul', 'unordered list'],\n onSelect: (editor) => {\n toggleIndentList(editor, {\n listStyleType: ListStyleType.Disc,\n });\n },\n value: 'Bulleted list',\n },\n {\n icon: Icons.ol,\n keywords: ['ol', 'ordered list'],\n onSelect: (editor) => {\n toggleIndentList(editor, {\n listStyleType: ListStyleType.Decimal,\n });\n },\n value: 'Numbered list',\n },\n];\n\nexport const SlashInputElement = withRef(\n ({ className, ...props }, ref) => {\n const { children, editor, element } = props;\n\n return (\n \n \n \n\n \n \n No matching commands found\n \n\n {rules.map(({ icon: Icon, keywords, onSelect, value }) => (\n onSelect(editor)}\n value={value}\n >\n \n {value}\n \n ))}\n \n \n\n {children}\n \n );\n }\n);\n", + "name": "slash-input-element.tsx" + } + ], + "name": "slash-input-element", + "registryDependencies": [ + "inline-combobox" + ], + "type": "components:plate-ui" +} \ No newline at end of file diff --git a/apps/www/src/__registry__/index.tsx b/apps/www/src/__registry__/index.tsx index f4734a4969..477cd9c008 100644 --- a/apps/www/src/__registry__/index.tsx +++ b/apps/www/src/__registry__/index.tsx @@ -256,6 +256,13 @@ export const Index: Record = { files: ['registry/default/plate-ui/emoji-picker-search-bar.tsx'], component: React.lazy(() => import('@/registry/default/plate-ui/emoji-picker-search-bar')), }, + 'emoji-input-element': { + name: 'emoji-input-element', + type: 'components:plate-ui', + registryDependencies: ["inline-combobox"], + files: ['registry/default/plate-ui/emoji-input-element.tsx'], + component: React.lazy(() => import('@/registry/default/plate-ui/emoji-input-element')), + }, 'align-dropdown-menu': { name: 'align-dropdown-menu', type: 'components:plate-ui', @@ -438,6 +445,13 @@ export const Index: Record = { files: ['registry/default/plate-ui/indent-toolbar-button.tsx'], component: React.lazy(() => import('@/registry/default/plate-ui/indent-toolbar-button')), }, + 'inline-combobox': { + name: 'inline-combobox', + type: 'components:plate-ui', + registryDependencies: [], + files: ['registry/default/plate-ui/inline-combobox.tsx'], + component: React.lazy(() => import('@/registry/default/plate-ui/inline-combobox')), + }, 'input': { name: 'input', type: 'components:plate-ui', @@ -536,10 +550,17 @@ export const Index: Record = { files: ['registry/default/plate-ui/mention-element.tsx'], component: React.lazy(() => import('@/registry/default/plate-ui/mention-element')), }, + 'mention-element': { + name: 'mention-element', + type: 'components:plate-ui', + registryDependencies: [], + files: ['registry/default/plate-ui/mention-element.tsx'], + component: React.lazy(() => import('@/registry/default/plate-ui/mention-element')), + }, 'mention-input-element': { name: 'mention-input-element', type: 'components:plate-ui', - registryDependencies: [], + registryDependencies: ["inline-combobox"], files: ['registry/default/plate-ui/mention-input-element.tsx'], component: React.lazy(() => import('@/registry/default/plate-ui/mention-input-element')), }, @@ -599,6 +620,13 @@ export const Index: Record = { files: ['registry/default/plate-ui/separator.tsx'], component: React.lazy(() => import('@/registry/default/plate-ui/separator')), }, + 'slash-input-element': { + name: 'slash-input-element', + type: 'components:plate-ui', + registryDependencies: ["inline-combobox"], + files: ['registry/default/plate-ui/slash-input-element.tsx'], + component: React.lazy(() => import('@/registry/default/plate-ui/slash-input-element')), + }, 'table-cell-element': { name: 'table-cell-element', type: 'components:plate-ui', diff --git a/apps/www/src/config/customizer-components.ts b/apps/www/src/config/customizer-components.ts index c02290e736..0a2206eeea 100644 --- a/apps/www/src/config/customizer-components.ts +++ b/apps/www/src/config/customizer-components.ts @@ -80,6 +80,11 @@ export const customizerComponents = { href: '/docs/components/emoji-dropdown-menu', title: 'Emoji Dropdown Menu', }, + emojiInputElement: { + href: '/docs/components/emoji-input-element', + label: 'Element', + title: 'Emoji Input', + }, emojiToolbarDropdown: { href: '/docs/components/emoji-toolbar-dropdown', title: 'Emoji Toolbar Dropdown', @@ -133,6 +138,10 @@ export const customizerComponents = { href: '/docs/components/indent-toolbar-button', title: 'Indent Toolbar Button', }, + inlineCombobox: { + href: '/docs/components/inline-combobox', + title: 'Inline Combobox', + }, input: { href: '/docs/components/input', title: 'Input' }, insertDropdownMenu: { href: '/docs/components/insert-dropdown-menu', @@ -182,10 +191,6 @@ export const customizerComponents = { href: '/docs/components/media-toolbar-button', title: 'Media Toolbar Button', }, - mentionCombobox: { - href: '/docs/components/mention-combobox', - title: 'Mention Combobox', - }, mentionElement: { href: '/docs/components/mention-element', label: 'Element', diff --git a/apps/www/src/config/customizer-items.ts b/apps/www/src/config/customizer-items.ts index a4f1d85db4..60f9395861 100644 --- a/apps/www/src/config/customizer-items.ts +++ b/apps/www/src/config/customizer-items.ts @@ -252,12 +252,6 @@ export const customizerItems: Record = { route: customizerComponents.mentionInputElement.href, usage: 'MentionInputElement', }, - { - id: 'mention-combobox', - label: 'MentionCombobox', - route: customizerComponents.mentionCombobox.href, - usage: 'MentionCombobox', - }, ], id: ELEMENT_MENTION, label: 'Mention', @@ -490,13 +484,12 @@ export const customizerItems: Record = { [KEY_EMOJI]: { badges: [customizerBadges.handler], components: [ - // { - // id: 'emoji-combobox', - // label: 'EmojiCombobox', - // pluginOptions: [`renderAfterEditable: EmojiCombobox,`], - // route: customizerComponents.emojiCombobox.href, - // usage: 'EmojiCombobox', - // }, + { + id: 'emoji-input-element', + label: 'EmojiInputElement', + route: customizerComponents.emojiInputElement.href, + usage: 'EmojiInputElement', + }, ], id: KEY_EMOJI, label: 'Emoji', diff --git a/apps/www/src/config/docs.ts b/apps/www/src/config/docs.ts index 0345fa92ee..684115a779 100644 --- a/apps/www/src/config/docs.ts +++ b/apps/www/src/config/docs.ts @@ -70,6 +70,7 @@ export const docsConfig: DocsConfig = { customizerComponents.draggable, customizerComponents.dropdownMenu, customizerComponents.emojiDropdownMenu, + customizerComponents.emojiInputElement, customizerComponents.emojiToolbarDropdown, customizerComponents.excalidrawElement, customizerComponents.fixedToolbar, @@ -82,6 +83,7 @@ export const docsConfig: DocsConfig = { customizerComponents.imageElement, customizerComponents.indentListToolbarButton, customizerComponents.indentToolbarButton, + customizerComponents.inlineCombobox, customizerComponents.input, customizerComponents.insertDropdownMenu, customizerComponents.kbdLeaf, @@ -95,7 +97,6 @@ export const docsConfig: DocsConfig = { customizerComponents.mediaEmbedElement, customizerComponents.mediaPopover, customizerComponents.mediaToolbarButton, - customizerComponents.mentionCombobox, customizerComponents.mentionElement, customizerComponents.mentionInputElement, customizerComponents.modeDropdownMenu, diff --git a/apps/www/src/registry/registry.ts b/apps/www/src/registry/registry.ts index c6158aac27..71494c3d84 100644 --- a/apps/www/src/registry/registry.ts +++ b/apps/www/src/registry/registry.ts @@ -105,6 +105,13 @@ const ui: Registry = [ type: 'components:plate-ui', }, + { + dependencies: ['@udecode/plate-emoji'], + files: ['plate-ui/emoji-input-element.tsx'], + name: 'emoji-input-element', + registryDependencies: ['inline-combobox'], + type: 'components:plate-ui', + }, { dependencies: ['@udecode/plate-alignment'], files: ['plate-ui/align-dropdown-menu.tsx'], @@ -298,6 +305,13 @@ const ui: Registry = [ registryDependencies: ['toolbar'], type: 'components:plate-ui', }, + { + dependencies: ['@ariakit/react', '@udecode/plate-combobox'], + files: ['plate-ui/inline-combobox.tsx'], + name: 'inline-combobox', + registryDependencies: [], + type: 'components:plate-ui', + }, { dependencies: [], files: ['plate-ui/input.tsx'], @@ -404,11 +418,18 @@ const ui: Registry = [ registryDependencies: [], type: 'components:plate-ui', }, + { + dependencies: ['@udecode/plate-mention'], + files: ['plate-ui/mention-element.tsx'], + name: 'mention-element', + registryDependencies: [], + type: 'components:plate-ui', + }, { dependencies: ['@udecode/plate-mention'], files: ['plate-ui/mention-input-element.tsx'], name: 'mention-input-element', - registryDependencies: [], + registryDependencies: ['inline-combobox'], type: 'components:plate-ui', }, { @@ -467,6 +488,13 @@ const ui: Registry = [ registryDependencies: [], type: 'components:plate-ui', }, + { + dependencies: ['@udecode/plate-heading', '@udecode/plate-indent-list'], + files: ['plate-ui/slash-input-element.tsx'], + name: 'slash-input-element', + registryDependencies: ['inline-combobox'], + type: 'components:plate-ui', + }, { dependencies: ['@udecode/plate-table'], files: ['plate-ui/table-cell-element.tsx'], From 54249402f71bd7b7e4385b94051a5ded663af8ba Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Wed, 5 Jun 2024 16:52:30 +0100 Subject: [PATCH 41/63] Linter and brl fixes --- packages/floating/src/hooks/index.ts | 4 +++- packages/floating/src/index.ts | 4 +++- packages/floating/src/libs/index.ts | 4 +++- packages/floating/src/utils/index.ts | 4 +++- packages/resizable/src/components/index.ts | 4 +++- packages/resizable/src/index.ts | 4 +++- packages/resizable/src/utils/index.ts | 4 +++- packages/slash-command/src/createSlashPlugin.ts | 1 + 8 files changed, 22 insertions(+), 7 deletions(-) diff --git a/packages/floating/src/hooks/index.ts b/packages/floating/src/hooks/index.ts index 562abb08fa..8b64ee9ef8 100644 --- a/packages/floating/src/hooks/index.ts +++ b/packages/floating/src/hooks/index.ts @@ -1,4 +1,6 @@ -/** @file Automatically generated by barrelsby. */ +/** + * @file Automatically generated by barrelsby. + */ export * from './useFloatingToolbar'; export * from './useVirtualFloating'; diff --git a/packages/floating/src/index.ts b/packages/floating/src/index.ts index 5b4ac112f9..a5baa29d2a 100644 --- a/packages/floating/src/index.ts +++ b/packages/floating/src/index.ts @@ -1,4 +1,6 @@ -/** @file Automatically generated by barrelsby. */ +/** + * @file Automatically generated by barrelsby. + */ export * from './createVirtualElement'; export * from './hooks/index'; diff --git a/packages/floating/src/libs/index.ts b/packages/floating/src/libs/index.ts index 3e88188e33..3d40627f8c 100644 --- a/packages/floating/src/libs/index.ts +++ b/packages/floating/src/libs/index.ts @@ -1,3 +1,5 @@ -/** @file Automatically generated by barrelsby. */ +/** + * @file Automatically generated by barrelsby. + */ export * from './floating-ui'; diff --git a/packages/floating/src/utils/index.ts b/packages/floating/src/utils/index.ts index c16dd4fc94..ec696367c1 100644 --- a/packages/floating/src/utils/index.ts +++ b/packages/floating/src/utils/index.ts @@ -1,4 +1,6 @@ -/** @file Automatically generated by barrelsby. */ +/** + * @file Automatically generated by barrelsby. + */ export * from './createVirtualRef'; export * from './getBoundingClientRect'; diff --git a/packages/resizable/src/components/index.ts b/packages/resizable/src/components/index.ts index c1ca39c240..9045f0c550 100644 --- a/packages/resizable/src/components/index.ts +++ b/packages/resizable/src/components/index.ts @@ -1,4 +1,6 @@ -/** @file Automatically generated by barrelsby. */ +/** + * @file Automatically generated by barrelsby. + */ export * from './Resizable'; export * from './ResizeHandle'; diff --git a/packages/resizable/src/index.ts b/packages/resizable/src/index.ts index d33888268d..2985a3c676 100644 --- a/packages/resizable/src/index.ts +++ b/packages/resizable/src/index.ts @@ -1,4 +1,6 @@ -/** @file Automatically generated by barrelsby. */ +/** + * @file Automatically generated by barrelsby. + */ export * from './types'; export * from './components/index'; diff --git a/packages/resizable/src/utils/index.ts b/packages/resizable/src/utils/index.ts index e06aecdbc3..2d84c8da91 100644 --- a/packages/resizable/src/utils/index.ts +++ b/packages/resizable/src/utils/index.ts @@ -1,4 +1,6 @@ -/** @file Automatically generated by barrelsby. */ +/** + * @file Automatically generated by barrelsby. + */ export * from './isTouchEvent'; export * from './resizeLengthClamp'; diff --git a/packages/slash-command/src/createSlashPlugin.ts b/packages/slash-command/src/createSlashPlugin.ts index 88315ca7bc..bd901b0468 100644 --- a/packages/slash-command/src/createSlashPlugin.ts +++ b/packages/slash-command/src/createSlashPlugin.ts @@ -5,6 +5,7 @@ import { import { createPluginFactory } from '@udecode/plate-common/server'; export const KEY_SLASH_COMMAND = 'slash_command'; + export const ELEMENT_SLASH_INPUT = 'slash_input'; export const createSlashPlugin = createPluginFactory({ From 6985cf69dcfaea030a8ddde260fe5dd5f989517a Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Wed, 5 Jun 2024 16:52:39 +0100 Subject: [PATCH 42/63] Add package changesets --- .changeset/combobox.md | 25 +++++++++++++++++++++++++ .changeset/emoji.md | 12 ++++++++++++ .changeset/mention.md | 18 ++++++++++++++++++ .changeset/slash.md | 20 ++++++++++++++++++++ 4 files changed, 75 insertions(+) create mode 100644 .changeset/combobox.md create mode 100644 .changeset/emoji.md create mode 100644 .changeset/mention.md create mode 100644 .changeset/slash.md diff --git a/.changeset/combobox.md b/.changeset/combobox.md new file mode 100644 index 0000000000..7bbf7302a1 --- /dev/null +++ b/.changeset/combobox.md @@ -0,0 +1,25 @@ +--- +"@udecode/plate-combobox": major +--- + +- Major rework. The combobox package is no longer a plugin. Instead, it is now a collection of utilities that can be used by other plugins and components. +- Added the following exports: + - `withTriggerCombobox`: Insert a combobox input when a trigger character is typed + - `TriggerComboboxPlugin`: Plugin options type for `withTriggerCombobox` + - `useComboboxInput`: Manages the behavior of an inline combobox input element + - `useHTMLInputCursorState`: Tracks whether the cursor is at the start or end of a HTML `` element + - `ComboboxInputCursorState`: Return type for `useHTMLInputCursorState` + - `CancelComboboxInputCause`: A unison type of possible reasons why a combobox input may be cancelled (used by `useComboboxInput`) +- Removed the following exports: + - `comboboxStore` + - `createComboboxPlugin` + - `useComboboxContent` + - `useComboboxControls` + - `useComboboxItem` + - `onChangeCombobox` + - `onKeyDownCombobox` + - `ComboboxOnSelectItem` + - `ComboboxProps` + - `getNextNonDisabledIndex` + - `getNextWrappingIndex` + - `getTextFromTrigger` diff --git a/.changeset/emoji.md b/.changeset/emoji.md new file mode 100644 index 0000000000..e03f662bc2 --- /dev/null +++ b/.changeset/emoji.md @@ -0,0 +1,12 @@ +--- +"@udecode/plate-emoji": major +--- + +- Now uses the reworked combobox package +- Added `ELEMENT_EMOJI_INPUT`; combobox functionality must now be handled in the component +- Plugin options: + - Now extends from `TriggerComboboxPlugin` + - Added `createEmojiNode` to support custom emoji nodes + - Removed `emojiTriggeringController` + - Removed `id` (no longer needed) + - Removed `createEmoji` diff --git a/.changeset/mention.md b/.changeset/mention.md new file mode 100644 index 0000000000..e94a788aea --- /dev/null +++ b/.changeset/mention.md @@ -0,0 +1,18 @@ +--- +"@udecode/plate-mention": major +--- + +- Now uses the reworked combobox package +- `ELEMENT_MENTION_INPUT` is now an inline void element, and combobox functionality must now be handled in the component +- Plugin options: + - Now extends from `TriggerComboboxPlugin` + - Renamed `query` to `triggerQuery` (provided by `TriggerComboboxPlugin`) + - Removed `id` (no longer needed) + - Removed `inputCreation` (see `TriggerComboboxPlugin['createComboboxInput']`) +- Removed queries and transforms relating to the mention input: + - `findMentionInput` + - `isNodeMentionInput` + - `isSelectionInMentionInput` + - `removeMentionInput` +- Removed `withMention` (no longer needed) +- Removed `mentionOnKeyDownHandler` (no longer needed) diff --git a/.changeset/slash.md b/.changeset/slash.md new file mode 100644 index 0000000000..0779c109a6 --- /dev/null +++ b/.changeset/slash.md @@ -0,0 +1,20 @@ +--- +"@udecode/plate-slash-command": major +--- + +- Now uses the reworked combobox package +- `ELEMENT_SLASH_INPUT` is now an inline void element, and combobox functionality must now be handled in the component +- Replaced all plugin options with those extended from `TriggerComboboxPlugin` + - Removed `createSlashNode` + - Removed `id` (no longer needed) + - Removed `inputCreation` (see `createComboboxInput`) + - Renamed `query` to `triggerQuery` (provided by `TriggerComboboxPlugin`) + - Removed `rules`: Slash command rules must now be provided in the component +- Removed queries and transforms relating to the slash input: + - `findSlashInput` + - `isNodeSlashInput` + - `isSelectionInSlashInput` + - `removeSlashInput` +- Removed `withSlashCommand` (no longer needed) +- Removed `slashOnKeyDownHandler` (no longer needed) +- Removed `getSlashOnSelectItem`: This should now be handled in the component From 783c8b69ab3afbc2803a75a559f4194b3894e141 Mon Sep 17 00:00:00 2001 From: FelixFeng Date: Thu, 6 Jun 2024 13:33:23 +0800 Subject: [PATCH 43/63] feat --- .changeset/blue-readers-judge.md | 5 + .changeset/breezy-beers-end.md | 5 + .changeset/four-goats-stare.md | 5 + .changeset/happy-masks-jump.md | 5 + .changeset/lazy-poets-buy.md | 5 + .changeset/long-lobsters-judge.md | 5 + .changeset/many-pillows-unite.md | 5 + .changeset/ninety-knives-wash.md | 5 + .changeset/pink-yaks-smoke.md | 5 + .changeset/smart-flies-relate.md | 5 + .changeset/thin-cycles-itch.md | 5 + .changeset/wicked-cheetahs-laugh.md | 5 + .changeset/wise-pandas-thank.md | 5 + .changeset/yellow-gifts-vanish.md | 5 + .changeset/young-pigs-behave.md | 5 + .changeset/young-sheep-rush.md | 5 + .../src/lib/plate/demo/plugins/alignPlugin.ts | 2 + .../default/example/playground-demo.tsx | 5 +- .../registry/default/plate-ui/draggable.tsx | 57 ++++++--- .../default/plate-ui/image-element.tsx | 1 + packages/callout/.npmignore | 3 + packages/callout/README.md | 7 + packages/callout/package.json | 61 +++++++++ packages/callout/src/createCalloutPlugin.ts | 14 ++ packages/callout/src/hooks/index.ts | 5 + packages/callout/src/hooks/useCallout.ts | 72 +++++++++++ packages/callout/src/index.ts | 7 + packages/callout/src/transforms/index.ts | 5 + .../callout/src/transforms/insertCallout.ts | 25 ++++ packages/callout/tsconfig.json | 8 ++ packages/caption/src/TCaptionElement.ts | 1 + packages/caption/src/components/Caption.tsx | 12 +- .../src/client/onKeyDownCodeBlock.ts | 5 + .../src/shared/transforms/toggleNodeType.ts | 14 +- packages/dnd/src/createDndPlugin.tsx | 26 +++- packages/dnd/src/dndStore.ts | 1 + .../floating/src/hooks/useFloatingToolbar.ts | 44 +++++-- packages/font/src/index.ts | 1 + packages/font/src/transforms/index.ts | 5 + .../src/transforms/setBlockBackgroundColor.ts | 16 +++ .../heading/src/{ => heading}/constants.ts | 0 .../src/{ => heading}/createHeadingPlugin.ts | 2 +- packages/heading/src/heading/index.ts | 7 + packages/heading/src/{ => heading}/types.ts | 0 packages/heading/src/index.ts | 6 +- packages/heading/src/toc/createTocPlugin.ts | 9 ++ packages/heading/src/toc/hooks/index.ts | 5 + .../heading/src/toc/hooks/useTocElement.ts | 89 +++++++++++++ packages/heading/src/toc/index.ts | 9 ++ packages/heading/src/toc/tocSideBar/index.ts | 7 + .../toc/tocSideBar/useContentController.ts | 86 +++++++++++++ .../src/toc/tocSideBar/useContentObserver.ts | 81 ++++++++++++ .../src/toc/tocSideBar/useTocController.ts | 38 ++++++ .../src/toc/tocSideBar/useTocObserver.ts | 62 +++++++++ .../src/toc/tocSideBar/useTocSideBarState.ts | 119 +++++++++++++++++ packages/heading/src/toc/transforms/index.ts | 5 + .../heading/src/toc/transforms/insertToc.ts | 23 ++++ packages/heading/src/toc/types.ts | 27 ++++ packages/heading/src/utils/checkIn.ts | 27 ++++ packages/heading/src/utils/getHeadingList.ts | 48 +++++++ packages/heading/src/utils/heightToTop.ts | 18 +++ packages/heading/src/utils/index.ts | 8 ++ packages/heading/src/utils/isHeading.ts | 7 + .../indent-list/src/transforms/indentList.ts | 2 + packages/indent-list/src/transforms/index.ts | 1 + .../src/transforms/toggleIndentListByPath.ts | 42 ++++++ packages/layout/src/onKeyDownColumn.ts | 29 ++++- packages/layout/src/transforms/index.ts | 1 + .../layout/src/transforms/toggleColumns.ts | 41 ++++++ .../src/components/useLinkToolbarButton.ts | 3 + packages/math/.npmignore | 3 + packages/math/README.md | 11 ++ packages/math/package.json | 65 ++++++++++ .../src/equation/components/EquationButton.ts | 27 ++++ .../src/equation/components/EquationInput.ts | 56 ++++++++ .../math/src/equation/components/index.ts | 6 + .../math/src/equation/createEquationPlugin.ts | 11 ++ packages/math/src/equation/hooks/index.ts | 5 + .../src/equation/hooks/useEquationElement.ts | 26 ++++ packages/math/src/equation/index.ts | 9 ++ .../math/src/equation/transforms/index.ts | 5 + .../src/equation/transforms/insertEquation.ts | 25 ++++ packages/math/src/equation/types.ts | 5 + packages/math/src/index.ts | 6 + .../components/InlineEquationButton.ts | 34 +++++ .../components/InlineEquationInput.ts | 121 ++++++++++++++++++ .../src/inline-equation/components/index.ts | 6 + .../createInlineEquationPlugin.ts | 10 ++ .../math/src/inline-equation/hooks/index.ts | 5 + .../hooks/useInlineEquationElement.ts | 26 ++++ packages/math/src/inline-equation/index.ts | 9 ++ .../src/inline-equation/transforms/index.ts | 5 + .../transforms/insertInlineEquation.ts | 25 ++++ packages/math/src/inline-equation/types.ts | 5 + .../utils/setSelectionInlineEquation.ts | 31 +++++ packages/math/tsconfig.build.json | 8 ++ packages/math/tsconfig.json | 5 + packages/media/src/audio/createAudioPlugin.ts | 11 ++ packages/media/src/audio/index.ts | 6 + packages/media/src/audio/types.ts | 5 + packages/media/src/file/createFilePlugin.ts | 11 ++ packages/media/src/file/index.ts | 6 + packages/media/src/file/types.ts | 5 + packages/media/src/index.ts | 4 + packages/media/src/media/types.ts | 3 + packages/media/src/media/useMediaState.ts | 10 +- .../placeholder/createPlaceholderPlugin.ts | 22 ++++ packages/media/src/placeholder/hooks/index.ts | 5 + .../hooks/usePlaceholderElement.ts | 36 ++++++ packages/media/src/placeholder/index.ts | 10 ++ .../media/src/placeholder/placeholderStore.ts | 18 +++ .../media/src/placeholder/popover/index.ts | 5 + .../popover/usePlaceholderPopover.ts | 46 +++++++ .../media/src/placeholder/transforms/index.ts | 6 + .../transforms/insertPlaceholder.ts | 35 +++++ .../placeholder/transforms/setMediaNode.ts | 19 +++ packages/media/src/placeholder/types.ts | 13 ++ packages/media/src/video/createVideoPlugin.ts | 11 ++ packages/media/src/video/index.ts | 6 + packages/media/src/video/types.ts | 5 + .../plate-utils/src/shared/addSelectedRow.ts | 8 ++ packages/plate-utils/src/shared/index.ts | 1 + packages/selection/src/blockSelectionStore.ts | 33 +++++ .../src/components/BlockSelectable.tsx | 56 +++++++- .../src/components/BlockSelection.tsx | 36 ++++++ .../src/components/BlockStartArea.tsx | 4 + packages/selection/src/components/index.ts | 1 + .../src/context-menu/blockContextMenuStore.ts | 46 +++++++ packages/selection/src/context-menu/index.ts | 8 ++ packages/selection/src/context-menu/types.ts | 14 ++ .../src/context-menu/useBlockContextMenu.ts | 67 ++++++++++ .../src/context-menu/useBlockMenuItems.ts | 24 ++++ .../src/createBlockSelectionPlugin.tsx | 15 ++- packages/selection/src/hooks/index.ts | 5 + .../selection/src/hooks/useBlockSelected.ts | 9 ++ packages/selection/src/index.ts | 5 +- .../selection/src/onChangeBlockSelection.ts | 16 --- packages/selection/src/onKeyDownSelection.ts | 48 +++++++ .../src/queries/getSelectedBlocks.ts | 6 + .../selection/src/useHooksBlockSelection.ts | 39 +++++- .../src/utils/extractSelectableIds.ts | 3 + .../selection/src/utils/getAboveDomNode.ts | 7 + packages/selection/src/utils/index.ts | 2 + .../src/utils/onCloseBlockSelection.ts | 25 ++++ packages/selection/src/withSelection.ts | 14 ++ .../src/queries/getAncestorNode.ts | 17 +++ .../slate-utils/src/queries/getNodesRange.ts | 19 +++ packages/slate-utils/src/queries/index.ts | 3 + .../src/queries/isSelectionCoverBlock.ts | 21 +++ yarn.lock | 59 +++++++++ 150 files changed, 2657 insertions(+), 81 deletions(-) create mode 100644 .changeset/blue-readers-judge.md create mode 100644 .changeset/breezy-beers-end.md create mode 100644 .changeset/four-goats-stare.md create mode 100644 .changeset/happy-masks-jump.md create mode 100644 .changeset/lazy-poets-buy.md create mode 100644 .changeset/long-lobsters-judge.md create mode 100644 .changeset/many-pillows-unite.md create mode 100644 .changeset/ninety-knives-wash.md create mode 100644 .changeset/pink-yaks-smoke.md create mode 100644 .changeset/smart-flies-relate.md create mode 100644 .changeset/thin-cycles-itch.md create mode 100644 .changeset/wicked-cheetahs-laugh.md create mode 100644 .changeset/wise-pandas-thank.md create mode 100644 .changeset/yellow-gifts-vanish.md create mode 100644 .changeset/young-pigs-behave.md create mode 100644 .changeset/young-sheep-rush.md create mode 100644 packages/callout/.npmignore create mode 100644 packages/callout/README.md create mode 100644 packages/callout/package.json create mode 100644 packages/callout/src/createCalloutPlugin.ts create mode 100644 packages/callout/src/hooks/index.ts create mode 100644 packages/callout/src/hooks/useCallout.ts create mode 100644 packages/callout/src/index.ts create mode 100644 packages/callout/src/transforms/index.ts create mode 100644 packages/callout/src/transforms/insertCallout.ts create mode 100644 packages/callout/tsconfig.json create mode 100644 packages/font/src/transforms/index.ts create mode 100644 packages/font/src/transforms/setBlockBackgroundColor.ts rename packages/heading/src/{ => heading}/constants.ts (100%) rename packages/heading/src/{ => heading}/createHeadingPlugin.ts (100%) create mode 100644 packages/heading/src/heading/index.ts rename packages/heading/src/{ => heading}/types.ts (100%) create mode 100644 packages/heading/src/toc/createTocPlugin.ts create mode 100644 packages/heading/src/toc/hooks/index.ts create mode 100644 packages/heading/src/toc/hooks/useTocElement.ts create mode 100644 packages/heading/src/toc/index.ts create mode 100644 packages/heading/src/toc/tocSideBar/index.ts create mode 100644 packages/heading/src/toc/tocSideBar/useContentController.ts create mode 100644 packages/heading/src/toc/tocSideBar/useContentObserver.ts create mode 100644 packages/heading/src/toc/tocSideBar/useTocController.ts create mode 100644 packages/heading/src/toc/tocSideBar/useTocObserver.ts create mode 100644 packages/heading/src/toc/tocSideBar/useTocSideBarState.ts create mode 100644 packages/heading/src/toc/transforms/index.ts create mode 100644 packages/heading/src/toc/transforms/insertToc.ts create mode 100644 packages/heading/src/toc/types.ts create mode 100644 packages/heading/src/utils/checkIn.ts create mode 100644 packages/heading/src/utils/getHeadingList.ts create mode 100644 packages/heading/src/utils/heightToTop.ts create mode 100644 packages/heading/src/utils/index.ts create mode 100644 packages/heading/src/utils/isHeading.ts create mode 100644 packages/indent-list/src/transforms/toggleIndentListByPath.ts create mode 100644 packages/layout/src/transforms/toggleColumns.ts create mode 100644 packages/math/.npmignore create mode 100644 packages/math/README.md create mode 100644 packages/math/package.json create mode 100644 packages/math/src/equation/components/EquationButton.ts create mode 100644 packages/math/src/equation/components/EquationInput.ts create mode 100644 packages/math/src/equation/components/index.ts create mode 100644 packages/math/src/equation/createEquationPlugin.ts create mode 100644 packages/math/src/equation/hooks/index.ts create mode 100644 packages/math/src/equation/hooks/useEquationElement.ts create mode 100644 packages/math/src/equation/index.ts create mode 100644 packages/math/src/equation/transforms/index.ts create mode 100644 packages/math/src/equation/transforms/insertEquation.ts create mode 100644 packages/math/src/equation/types.ts create mode 100644 packages/math/src/index.ts create mode 100644 packages/math/src/inline-equation/components/InlineEquationButton.ts create mode 100644 packages/math/src/inline-equation/components/InlineEquationInput.ts create mode 100644 packages/math/src/inline-equation/components/index.ts create mode 100644 packages/math/src/inline-equation/createInlineEquationPlugin.ts create mode 100644 packages/math/src/inline-equation/hooks/index.ts create mode 100644 packages/math/src/inline-equation/hooks/useInlineEquationElement.ts create mode 100644 packages/math/src/inline-equation/index.ts create mode 100644 packages/math/src/inline-equation/transforms/index.ts create mode 100644 packages/math/src/inline-equation/transforms/insertInlineEquation.ts create mode 100644 packages/math/src/inline-equation/types.ts create mode 100644 packages/math/src/inline-equation/utils/setSelectionInlineEquation.ts create mode 100644 packages/math/tsconfig.build.json create mode 100644 packages/math/tsconfig.json create mode 100644 packages/media/src/audio/createAudioPlugin.ts create mode 100644 packages/media/src/audio/index.ts create mode 100644 packages/media/src/audio/types.ts create mode 100644 packages/media/src/file/createFilePlugin.ts create mode 100644 packages/media/src/file/index.ts create mode 100644 packages/media/src/file/types.ts create mode 100644 packages/media/src/placeholder/createPlaceholderPlugin.ts create mode 100644 packages/media/src/placeholder/hooks/index.ts create mode 100644 packages/media/src/placeholder/hooks/usePlaceholderElement.ts create mode 100644 packages/media/src/placeholder/index.ts create mode 100644 packages/media/src/placeholder/placeholderStore.ts create mode 100644 packages/media/src/placeholder/popover/index.ts create mode 100644 packages/media/src/placeholder/popover/usePlaceholderPopover.ts create mode 100644 packages/media/src/placeholder/transforms/index.ts create mode 100644 packages/media/src/placeholder/transforms/insertPlaceholder.ts create mode 100644 packages/media/src/placeholder/transforms/setMediaNode.ts create mode 100644 packages/media/src/placeholder/types.ts create mode 100644 packages/media/src/video/createVideoPlugin.ts create mode 100644 packages/media/src/video/index.ts create mode 100644 packages/media/src/video/types.ts create mode 100644 packages/plate-utils/src/shared/addSelectedRow.ts create mode 100644 packages/selection/src/components/BlockSelection.tsx create mode 100644 packages/selection/src/context-menu/blockContextMenuStore.ts create mode 100644 packages/selection/src/context-menu/index.ts create mode 100644 packages/selection/src/context-menu/types.ts create mode 100644 packages/selection/src/context-menu/useBlockContextMenu.ts create mode 100644 packages/selection/src/context-menu/useBlockMenuItems.ts create mode 100644 packages/selection/src/hooks/index.ts create mode 100644 packages/selection/src/hooks/useBlockSelected.ts delete mode 100644 packages/selection/src/onChangeBlockSelection.ts create mode 100644 packages/selection/src/onKeyDownSelection.ts create mode 100644 packages/selection/src/utils/getAboveDomNode.ts create mode 100644 packages/selection/src/utils/onCloseBlockSelection.ts create mode 100644 packages/selection/src/withSelection.ts create mode 100644 packages/slate-utils/src/queries/getAncestorNode.ts create mode 100644 packages/slate-utils/src/queries/getNodesRange.ts create mode 100644 packages/slate-utils/src/queries/isSelectionCoverBlock.ts diff --git a/.changeset/blue-readers-judge.md b/.changeset/blue-readers-judge.md new file mode 100644 index 0000000000..f0dc43271a --- /dev/null +++ b/.changeset/blue-readers-judge.md @@ -0,0 +1,5 @@ +--- +"@udecode/plate-caption": major +--- + +The show of the caption needs to be controlled through `showCaption`. diff --git a/.changeset/breezy-beers-end.md b/.changeset/breezy-beers-end.md new file mode 100644 index 0000000000..66e31f6468 --- /dev/null +++ b/.changeset/breezy-beers-end.md @@ -0,0 +1,5 @@ +--- +"@udecode/plate-floating": patch +--- + +Only pop up `floating-toolbar` after the selection is complete. diff --git a/.changeset/four-goats-stare.md b/.changeset/four-goats-stare.md new file mode 100644 index 0000000000..6d7583ff98 --- /dev/null +++ b/.changeset/four-goats-stare.md @@ -0,0 +1,5 @@ +--- +"@udecode/plate-font": minor +--- + +add new transformas `setBlockBackgroundColor`. diff --git a/.changeset/happy-masks-jump.md b/.changeset/happy-masks-jump.md new file mode 100644 index 0000000000..dd4b5341d8 --- /dev/null +++ b/.changeset/happy-masks-jump.md @@ -0,0 +1,5 @@ +--- +"@udecode/plate-layout": minor +--- + +add `toggleColumns` and fix select all. diff --git a/.changeset/lazy-poets-buy.md b/.changeset/lazy-poets-buy.md new file mode 100644 index 0000000000..17a7df3ea9 --- /dev/null +++ b/.changeset/lazy-poets-buy.md @@ -0,0 +1,5 @@ +--- +"@udecode/plate-selection": minor +--- + +Added logic for the `block-context-menu` and improved the user experience for `block-selection`, such as interactions related to keyboard shortcuts , bug fixes. diff --git a/.changeset/long-lobsters-judge.md b/.changeset/long-lobsters-judge.md new file mode 100644 index 0000000000..ffe4cc69b3 --- /dev/null +++ b/.changeset/long-lobsters-judge.md @@ -0,0 +1,5 @@ +--- +"@udecode/plate-heading": minor +--- + +add `Toc Plugin` and `Toc Sidebar` diff --git a/.changeset/many-pillows-unite.md b/.changeset/many-pillows-unite.md new file mode 100644 index 0000000000..c70f383c3e --- /dev/null +++ b/.changeset/many-pillows-unite.md @@ -0,0 +1,5 @@ +--- +"@udecode/plate-math": minor +--- + +new plugins `equation` and `inlineEquation`. diff --git a/.changeset/ninety-knives-wash.md b/.changeset/ninety-knives-wash.md new file mode 100644 index 0000000000..0065bfbdc3 --- /dev/null +++ b/.changeset/ninety-knives-wash.md @@ -0,0 +1,5 @@ +--- +"@udecode/slate-utils": minor +--- + +add new queryies utils `getAncestorNode` and `getNodesRange` diff --git a/.changeset/pink-yaks-smoke.md b/.changeset/pink-yaks-smoke.md new file mode 100644 index 0000000000..b60252cf02 --- /dev/null +++ b/.changeset/pink-yaks-smoke.md @@ -0,0 +1,5 @@ +--- +"@udecode/plate-dnd": minor +--- + +add selection after dragging ends. diff --git a/.changeset/smart-flies-relate.md b/.changeset/smart-flies-relate.md new file mode 100644 index 0000000000..ad7babdf7a --- /dev/null +++ b/.changeset/smart-flies-relate.md @@ -0,0 +1,5 @@ +--- +"@udecode/plate-callout": minor +--- + +add new plugin `callout`. diff --git a/.changeset/thin-cycles-itch.md b/.changeset/thin-cycles-itch.md new file mode 100644 index 0000000000..a9e2a6fbdb --- /dev/null +++ b/.changeset/thin-cycles-itch.md @@ -0,0 +1,5 @@ +--- +"@udecode/plate-indent-list": patch +--- + +add `toggleIndentListByPath` ,`indentList` add at option diff --git a/.changeset/wicked-cheetahs-laugh.md b/.changeset/wicked-cheetahs-laugh.md new file mode 100644 index 0000000000..09e6c546ef --- /dev/null +++ b/.changeset/wicked-cheetahs-laugh.md @@ -0,0 +1,5 @@ +--- +"@udecode/plate-utils": minor +--- + +add `addSelectedRow` which depend on blockSelection plugin diff --git a/.changeset/wise-pandas-thank.md b/.changeset/wise-pandas-thank.md new file mode 100644 index 0000000000..8057cbd0ab --- /dev/null +++ b/.changeset/wise-pandas-thank.md @@ -0,0 +1,5 @@ +--- +"@udecode/plate-link": patch +--- + +fix link button make floating toolbar close. diff --git a/.changeset/yellow-gifts-vanish.md b/.changeset/yellow-gifts-vanish.md new file mode 100644 index 0000000000..0d26c31710 --- /dev/null +++ b/.changeset/yellow-gifts-vanish.md @@ -0,0 +1,5 @@ +--- +"@udecode/plate-core": patch +--- + +Fix the issue where the `toggleNodeType` function does not work when passing `at`. diff --git a/.changeset/young-pigs-behave.md b/.changeset/young-pigs-behave.md new file mode 100644 index 0000000000..a49cbea8ee --- /dev/null +++ b/.changeset/young-pigs-behave.md @@ -0,0 +1,5 @@ +--- +"@udecode/plate-media": minor +--- + +add plugins `mediaPlaceholder`, `video`,`audio` and `file`. diff --git a/.changeset/young-sheep-rush.md b/.changeset/young-sheep-rush.md new file mode 100644 index 0000000000..ec09245b3d --- /dev/null +++ b/.changeset/young-sheep-rush.md @@ -0,0 +1,5 @@ +--- +"@udecode/plate-code-block": patch +--- + +fix selection all diff --git a/apps/www/src/lib/plate/demo/plugins/alignPlugin.ts b/apps/www/src/lib/plate/demo/plugins/alignPlugin.ts index 242531b114..3e68249b5a 100644 --- a/apps/www/src/lib/plate/demo/plugins/alignPlugin.ts +++ b/apps/www/src/lib/plate/demo/plugins/alignPlugin.ts @@ -8,6 +8,7 @@ import { ELEMENT_H5, ELEMENT_H6, } from '@udecode/plate-heading'; +import { ELEMENT_IMAGE } from '@udecode/plate-media'; import { ELEMENT_PARAGRAPH } from '@udecode/plate-paragraph'; export const alignPlugin: Partial = { @@ -20,6 +21,7 @@ export const alignPlugin: Partial = { ELEMENT_H3, ELEMENT_H4, ELEMENT_H5, + ELEMENT_IMAGE, ELEMENT_H6, ], }, diff --git a/apps/www/src/registry/default/example/playground-demo.tsx b/apps/www/src/registry/default/example/playground-demo.tsx index bc3648bbd0..a5ca9d4178 100644 --- a/apps/www/src/registry/default/example/playground-demo.tsx +++ b/apps/www/src/registry/default/example/playground-demo.tsx @@ -203,7 +203,10 @@ export const usePlaygroundPlugins = ({ createKbdPlugin({ enabled: !!enabled.kbd }), // Block Style - createAlignPlugin({ ...alignPlugin, enabled: !!enabled.align }), + createAlignPlugin({ + ...alignPlugin, + enabled: !!enabled.align, + }), createIndentPlugin({ enabled: !!enabled.indent, inject: { diff --git a/apps/www/src/registry/default/plate-ui/draggable.tsx b/apps/www/src/registry/default/plate-ui/draggable.tsx index 983f3beb0f..1c5cdeac21 100644 --- a/apps/www/src/registry/default/plate-ui/draggable.tsx +++ b/apps/www/src/registry/default/plate-ui/draggable.tsx @@ -6,19 +6,19 @@ import type { ClassNames, PlateElementProps, TEditor, + TElement, } from '@udecode/plate-common'; +import type { DragItemNode } from '@udecode/plate-dnd'; import type { DropTargetMonitor } from 'react-dnd'; import { cn, withRef } from '@udecode/cn'; -import { - type DragItemNode, - useDraggable, - useDraggableState, -} from '@udecode/plate-dnd'; +import { useEditorRef, useElement } from '@udecode/plate-common'; +import { useDraggable, useDraggableState } from '@udecode/plate-dnd'; +import { blockSelectionActions } from '@udecode/plate-selection'; import { Icons } from '@/components/icons'; -import { Tooltip, TooltipContent, TooltipPortal, TooltipTrigger } from './tooltip'; +import { Tooltip, TooltipContent, TooltipTrigger } from './tooltip'; export interface DraggableProps extends PlateElementProps, @@ -68,16 +68,33 @@ export interface DraggableProps ) => boolean; } -const dragHandle = ( - - - - - +const DragHandle = () => { + const editor = useEditorRef(); + const element = useElement(); + + return ( + + + { + event.stopPropagation(); + event.preventDefault(); + + // if (element.id) { + // blockSelectionActions.addSelectedRow(element.id as string); + // blockContextMenuActions.show(editor.id, event as any); + // } + }} + onMouseDown={() => { + blockSelectionActions.resetSelectedIds(); + }} + /> + Drag to move - - -); + + ); +}; export const Draggable = withRef<'div', DraggableProps>( ({ className, classNames = {}, onDropHandler, ...props }, ref) => { @@ -106,7 +123,7 @@ export const Draggable = withRef<'div', DraggableProps>( >
( classNames.blockToolbar )} > -
- {isHovered && dragHandle} +
+ {isHovered && }
diff --git a/apps/www/src/registry/default/plate-ui/image-element.tsx b/apps/www/src/registry/default/plate-ui/image-element.tsx index 94e8ecfcad..069927975a 100644 --- a/apps/www/src/registry/default/plate-ui/image-element.tsx +++ b/apps/www/src/registry/default/plate-ui/image-element.tsx @@ -18,6 +18,7 @@ export const ImageElement = withHOC( withRef( ({ children, className, nodeProps, ...props }, ref) => { const { align = 'center', focused, readOnly, selected } = useMediaState(); + const width = useResizableStore().get.width(); return ( diff --git a/packages/callout/.npmignore b/packages/callout/.npmignore new file mode 100644 index 0000000000..7d3b305b17 --- /dev/null +++ b/packages/callout/.npmignore @@ -0,0 +1,3 @@ +__tests__ +__test-utils__ +__mocks__ diff --git a/packages/callout/README.md b/packages/callout/README.md new file mode 100644 index 0000000000..1f0ba51d97 --- /dev/null +++ b/packages/callout/README.md @@ -0,0 +1,7 @@ +# Plate callout plugins + +This package implements th callout block plugins for Plate. + +## License + +[MIT](../../LICENSE) diff --git a/packages/callout/package.json b/packages/callout/package.json new file mode 100644 index 0000000000..65c036ec14 --- /dev/null +++ b/packages/callout/package.json @@ -0,0 +1,61 @@ +{ + "name": "@udecode/plate-callout", + "version": "31.0.0", + "description": "Callout plugin for Plate", + "license": "MIT", + "homepage": "https://platejs.org", + "repository": { + "type": "git", + "url": "https://github.com/udecode/plate.git", + "directory": "packages/callout" + }, + "bugs": { + "url": "https://github.com/udecode/plate/issues" + }, + "sideEffects": false, + "main": "dist/index.js", + "module": "dist/index.mjs", + "types": "dist/index.d.ts", + "files": [ + "dist/**/*" + ], + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "module": "./dist/index.mjs", + "require": "./dist/index.js" + } + }, + "scripts": { + "build": "yarn p:build", + "build:watch": "yarn p:build:watch", + "brl": "yarn p:brl", + "clean": "yarn p:clean", + "lint": "yarn p:lint", + "lint:fix": "yarn p:lint:fix", + "test": "yarn p:test", + "test:watch": "yarn p:test:watch", + "typecheck": "yarn p:typecheck" + }, + "devDependencies": { + "@udecode/plate-common": "workspace:^" + }, + "peerDependencies": { + "@udecode/plate-common": ">=31.3.2", + "react": ">=16.8.0", + "react-dom": ">=16.8.0", + "slate": ">=0.94.0", + "slate-history": ">=0.93.0", + "slate-hyperscript": ">=0.66.0", + "slate-react": ">=0.99.0" + }, + "keywords": [ + "plate", + "plugin", + "slate" + ], + "publishConfig": { + "access": "public" + } +} diff --git a/packages/callout/src/createCalloutPlugin.ts b/packages/callout/src/createCalloutPlugin.ts new file mode 100644 index 0000000000..5ecb624fc2 --- /dev/null +++ b/packages/callout/src/createCalloutPlugin.ts @@ -0,0 +1,14 @@ +import { createPluginFactory } from '@udecode/plate-common'; + +export const ELEMENT_CALLOUT = 'callout'; + +export type CalloutColor = { + bgColor: string; + borderColor: string; + textColor: string; +}; + +export const createCalloutPlugin = createPluginFactory({ + isElement: true, + key: ELEMENT_CALLOUT, +}); diff --git a/packages/callout/src/hooks/index.ts b/packages/callout/src/hooks/index.ts new file mode 100644 index 0000000000..b04385e133 --- /dev/null +++ b/packages/callout/src/hooks/index.ts @@ -0,0 +1,5 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './useCallout'; diff --git a/packages/callout/src/hooks/useCallout.ts b/packages/callout/src/hooks/useCallout.ts new file mode 100644 index 0000000000..5a2c52e805 --- /dev/null +++ b/packages/callout/src/hooks/useCallout.ts @@ -0,0 +1,72 @@ +import React from 'react'; + +import { + type TElement, + findNodePath, + setNodes, + useEditorRef, + useElement, +} from '@udecode/plate-common'; + +export interface TCalloutElement extends TElement { + backgroundColor?: string; + color?: string; + icon?: string; + variant?: 'info' | 'note' | 'tip' | 'warning'; +} + +export const useCalloutState = () => { + const editor = useEditorRef(); + const element = useElement(); + + const setCalloutState = React.useCallback( + (value: Partial) => { + const path = findNodePath(editor, element); + + if (!path) return; + + setNodes(editor, value, { at: path }); + }, + [editor, element] + ); + + return { + icon: element.icon, + id: element.id, + setCalloutState, + variant: element.variant, + }; +}; + +interface useCalloutEmojiPick { + isOpen: boolean; + readOnly: boolean; + setCalloutState: any; + setIsOpen: (isOpen: boolean) => void; +} + +export const useCalloutEmojiPick = ({ + isOpen, + readOnly, + setCalloutState, + setIsOpen, +}: useCalloutEmojiPick) => { + return { + dropDownProps: { + isOpen, + setIsOpen: (v: boolean) => { + if (readOnly) return; + + setIsOpen(v); + }, + }, + props: { + isOpen, + onSelectEmoji: (emojiValue: any) => { + setCalloutState({ icon: emojiValue.skins[0].native }); + setIsOpen(false); + }, + setIsOpen, + }, + }; +}; diff --git a/packages/callout/src/index.ts b/packages/callout/src/index.ts new file mode 100644 index 0000000000..d51b4238d9 --- /dev/null +++ b/packages/callout/src/index.ts @@ -0,0 +1,7 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './createCalloutPlugin'; +export * from './hooks/index'; +export * from './transforms/index'; diff --git a/packages/callout/src/transforms/index.ts b/packages/callout/src/transforms/index.ts new file mode 100644 index 0000000000..9237eb8c3d --- /dev/null +++ b/packages/callout/src/transforms/index.ts @@ -0,0 +1,5 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './insertCallout'; diff --git a/packages/callout/src/transforms/insertCallout.ts b/packages/callout/src/transforms/insertCallout.ts new file mode 100644 index 0000000000..397d4cfe13 --- /dev/null +++ b/packages/callout/src/transforms/insertCallout.ts @@ -0,0 +1,25 @@ +import { + type InsertNodesOptions, + type PlateEditor, + type Value, + insertNodes, +} from '@udecode/plate-common'; + +import type { TCalloutElement } from '../hooks'; + +import { ELEMENT_CALLOUT } from '../createCalloutPlugin'; + +export const insertCallout = ( + editor: PlateEditor, + options?: InsertNodesOptions +) => { + insertNodes( + editor, + { + children: [{ text: '' }], + type: ELEMENT_CALLOUT, + variant: 'info', + }, + options as any + ); +}; diff --git a/packages/callout/tsconfig.json b/packages/callout/tsconfig.json new file mode 100644 index 0000000000..425481e027 --- /dev/null +++ b/packages/callout/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../config/tsconfig.build.json", + "compilerOptions": { + "declarationDir": "./dist", + "outDir": "./dist" + }, + "include": ["src"] +} diff --git a/packages/caption/src/TCaptionElement.ts b/packages/caption/src/TCaptionElement.ts index 8a23dea24a..2782a1f3b2 100644 --- a/packages/caption/src/TCaptionElement.ts +++ b/packages/caption/src/TCaptionElement.ts @@ -2,4 +2,5 @@ import type { TDescendant, TElement } from '@udecode/plate-common'; export interface TCaptionElement extends TElement { caption?: TDescendant[]; + showCaption?: boolean; } diff --git a/packages/caption/src/components/Caption.tsx b/packages/caption/src/components/Caption.tsx index cfad29625d..6f1462f861 100644 --- a/packages/caption/src/components/Caption.tsx +++ b/packages/caption/src/components/Caption.tsx @@ -1,8 +1,10 @@ import type React from 'react'; -import { createPrimitiveComponent } from '@udecode/plate-common'; +import { createPrimitiveComponent, useElement } from '@udecode/plate-common'; import { useReadOnly, useSelected } from 'slate-react'; +import type { TCaptionElement } from '../TCaptionElement'; + import { useCaptionString } from '../hooks/useCaptionString'; export interface CaptionOptions { @@ -16,13 +18,18 @@ export interface CaptionProps export const useCaptionState = (options: CaptionOptions = {}) => { const captionString = useCaptionString(); + const { showCaption } = useElement(); const selected = useSelected(); const _readOnly = useReadOnly(); const readOnly = options.readOnly || _readOnly; + const hidden = + !showCaption || (captionString.length === 0 && (readOnly || !selected)); + return { captionString, + hidden, readOnly, selected, }; @@ -30,8 +37,7 @@ export const useCaptionState = (options: CaptionOptions = {}) => { export const useCaption = (state: ReturnType) => { return { - hidden: - state.captionString.length === 0 && (state.readOnly || !state.selected), + hidden: state.hidden, }; }; diff --git a/packages/code-block/src/client/onKeyDownCodeBlock.ts b/packages/code-block/src/client/onKeyDownCodeBlock.ts index 4a4cf91f4e..903fb489e3 100644 --- a/packages/code-block/src/client/onKeyDownCodeBlock.ts +++ b/packages/code-block/src/client/onKeyDownCodeBlock.ts @@ -7,6 +7,8 @@ import { getNodeEntries, getParentNode, isHotkey, + isSelectionAtBlockEnd, + isSelectionAtBlockStart, select, withoutNormalizing, } from '@udecode/plate-common/server'; @@ -64,6 +66,9 @@ export const onKeyDownCodeBlock = const { codeBlock } = res; const [, codeBlockPath] = codeBlock; + if (isSelectionAtBlockEnd(editor) && isSelectionAtBlockStart(editor)) + return; + // select the whole code block select(editor, codeBlockPath); diff --git a/packages/core/src/shared/transforms/toggleNodeType.ts b/packages/core/src/shared/transforms/toggleNodeType.ts index a1b4039a00..2e50aa1d9b 100644 --- a/packages/core/src/shared/transforms/toggleNodeType.ts +++ b/packages/core/src/shared/transforms/toggleNodeType.ts @@ -36,7 +36,9 @@ export const toggleNodeType = ( const { activeType, inactiveType = getPluginType(editor, ELEMENT_DEFAULT) } = options; - if (!activeType || !editor.selection) return; + const at = editorNodesOptions?.at ?? editor.selection; + + if (!activeType || !at) return; const isActive = someNode(editor, { ...editorNodesOptions, @@ -47,7 +49,11 @@ export const toggleNodeType = ( if (isActive && activeType === inactiveType) return; - setElements(editor, { - type: isActive ? inactiveType : activeType, - }); + setElements( + editor, + { + type: isActive ? inactiveType : activeType, + }, + { at: at as any } + ); }; diff --git a/packages/dnd/src/createDndPlugin.tsx b/packages/dnd/src/createDndPlugin.tsx index f9a92de14c..6e20f92a6f 100644 --- a/packages/dnd/src/createDndPlugin.tsx +++ b/packages/dnd/src/createDndPlugin.tsx @@ -1,6 +1,9 @@ import React from 'react'; -import { createPluginFactory } from '@udecode/plate-common/server'; +import { + addSelectedRow, + createPluginFactory, +} from '@udecode/plate-common/server'; import { DndScroller, type ScrollerProps } from './components/Scroller'; import { dndStore } from './dndStore'; @@ -14,9 +17,24 @@ export const KEY_DND = 'dnd'; export const createDndPlugin = createPluginFactory({ handlers: { - onDragEnd: () => () => dndStore.set.isDragging(false), - onDragStart: () => () => dndStore.set.isDragging(true), - onDrop: (editor) => () => editor.isDragging as boolean, + onDragEnd: () => () => { + return dndStore.set.isDragging(false); + }, + onDragStart: () => (e) => { + const id = (e.target as HTMLDivElement).dataset.key ?? null; + dndStore.set.DraggingId(id); + + return dndStore.set.isDragging(true); + }, + onDrop: (editor) => () => { + const id = dndStore.get.DraggingId(); + + setTimeout(() => { + id && addSelectedRow(editor, id); + }, 0); + + return editor.isDragging as boolean; + }, }, key: KEY_DND, then: (editor, { options }) => ({ diff --git a/packages/dnd/src/dndStore.ts b/packages/dnd/src/dndStore.ts index e2c15f14fa..30dd779342 100644 --- a/packages/dnd/src/dndStore.ts +++ b/packages/dnd/src/dndStore.ts @@ -1,5 +1,6 @@ import { createZustandStore } from '@udecode/plate-common/server'; export const dndStore = createZustandStore('dnd')({ + DraggingId: null as null | string, isDragging: false, }); diff --git a/packages/floating/src/hooks/useFloatingToolbar.ts b/packages/floating/src/hooks/useFloatingToolbar.ts index e1e4aeb207..973508635c 100644 --- a/packages/floating/src/hooks/useFloatingToolbar.ts +++ b/packages/floating/src/hooks/useFloatingToolbar.ts @@ -1,11 +1,10 @@ import React from 'react'; -import { useEditorSelector, usePlateSelectors } from '@udecode/plate-common'; +import { useEditorSelector } from '@udecode/plate-common'; import { getSelectionText, isSelectionExpanded, mergeProps, - useEventEditorSelectors, } from '@udecode/plate-common/server'; import { useFocused } from 'slate-react'; @@ -22,20 +21,24 @@ export type FloatingToolbarState = { }; export const useFloatingToolbarState = ({ + editorId, floatingOptions, + focusedEditorId, hideToolbar, ignoreReadOnly, -}: FloatingToolbarState) => { - const editorId = usePlateSelectors().id(); +}: { + editorId: string; + focusedEditorId: null | string; +} & FloatingToolbarState) => { const selectionExpanded = useEditorSelector(isSelectionExpanded, []); const selectionText = useEditorSelector(getSelectionText, []); - const focusedEditorId = useEventEditorSelectors.focus(); const focused = useFocused(); const [open, setOpen] = React.useState(false); const [waitForCollapsedSelection, setWaitForCollapsedSelection] = React.useState(false); + const [mouseupped, setMouseupped] = React.useState(false); const floating = useVirtualFloating( mergeProps( @@ -55,9 +58,11 @@ export const useFloatingToolbarState = ({ focusedEditorId, hideToolbar, ignoreReadOnly, + mouseupped, open, selectionExpanded, selectionText, + setMouseupped, setOpen, setWaitForCollapsedSelection, waitForCollapsedSelection, @@ -67,13 +72,14 @@ export const useFloatingToolbarState = ({ export const useFloatingToolbar = ({ editorId, floating, - focused, focusedEditorId, hideToolbar, ignoreReadOnly, + mouseupped, open, selectionExpanded, selectionText, + setMouseupped, setOpen, setWaitForCollapsedSelection, waitForCollapsedSelection, @@ -81,29 +87,47 @@ export const useFloatingToolbar = ({ // On refocus, the editor keeps the previous selection, // so we need to wait it's collapsed at the new position before displaying the floating toolbar. React.useEffect(() => { - if (!focused || ignoreReadOnly) { + if (!(editorId === focusedEditorId) || ignoreReadOnly) { setWaitForCollapsedSelection(true); } if (!selectionExpanded) { setWaitForCollapsedSelection(false); } }, [ - focused, + editorId, + focusedEditorId, ignoreReadOnly, selectionExpanded, setWaitForCollapsedSelection, ]); + React.useEffect(() => { + const mouseup = () => setMouseupped(true); + + if (selectionText && selectionExpanded) { + document.addEventListener('mouseup', mouseup); + } else { + document.removeEventListener('mouseup', mouseup); + } + + return () => { + document.removeEventListener('mouseup', mouseup); + }; + }, [selectionText, selectionExpanded, setMouseupped]); + React.useEffect(() => { if ( !selectionExpanded || !selectionText || - (!(editorId === focusedEditorId || ignoreReadOnly) && hideToolbar) + // (!(editorId === focusedEditorId || ignoreReadOnly) && hideToolbar) + !(editorId === focusedEditorId) ) { setOpen(false); + setMouseupped(false); } else if ( selectionText && selectionExpanded && + mouseupped && !waitForCollapsedSelection ) { setOpen(true); @@ -116,6 +140,8 @@ export const useFloatingToolbar = ({ ignoreReadOnly, selectionExpanded, selectionText, + mouseupped, + setMouseupped, waitForCollapsedSelection, ]); diff --git a/packages/font/src/index.ts b/packages/font/src/index.ts index 9b91fc73c9..821cd3db57 100644 --- a/packages/font/src/index.ts +++ b/packages/font/src/index.ts @@ -8,3 +8,4 @@ export * from './createFontFamilyPlugin'; export * from './createFontSizePlugin'; export * from './createFontWeightPlugin'; export * from './hooks/index'; +export * from './transforms/index'; diff --git a/packages/font/src/transforms/index.ts b/packages/font/src/transforms/index.ts new file mode 100644 index 0000000000..f8ab53f787 --- /dev/null +++ b/packages/font/src/transforms/index.ts @@ -0,0 +1,5 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './setBlockBackgroundColor'; diff --git a/packages/font/src/transforms/setBlockBackgroundColor.ts b/packages/font/src/transforms/setBlockBackgroundColor.ts new file mode 100644 index 0000000000..1c5696dbc4 --- /dev/null +++ b/packages/font/src/transforms/setBlockBackgroundColor.ts @@ -0,0 +1,16 @@ +import { + type PlateEditor, + type TNodeEntry, + type Value, + setNodes, +} from '@udecode/plate-common'; + +import { MARK_BG_COLOR } from '../createFontBackgroundColorPlugin'; + +export const setBlockBackgroundColor = ( + editor: PlateEditor, + block: TNodeEntry, + backgroundColor: string +) => { + setNodes(editor, { [MARK_BG_COLOR]: backgroundColor }, { at: block[1] }); +}; diff --git a/packages/heading/src/constants.ts b/packages/heading/src/heading/constants.ts similarity index 100% rename from packages/heading/src/constants.ts rename to packages/heading/src/heading/constants.ts diff --git a/packages/heading/src/createHeadingPlugin.ts b/packages/heading/src/heading/createHeadingPlugin.ts similarity index 100% rename from packages/heading/src/createHeadingPlugin.ts rename to packages/heading/src/heading/createHeadingPlugin.ts index 0a8fe8b864..72e2548ec3 100644 --- a/packages/heading/src/createHeadingPlugin.ts +++ b/packages/heading/src/heading/createHeadingPlugin.ts @@ -1,7 +1,7 @@ import { - onKeyDownToggleElement, type PlatePlugin, createPluginFactory, + onKeyDownToggleElement, } from '@udecode/plate-common/server'; import type { HeadingPlugin, HeadingsPlugin } from './types'; diff --git a/packages/heading/src/heading/index.ts b/packages/heading/src/heading/index.ts new file mode 100644 index 0000000000..2bdea46920 --- /dev/null +++ b/packages/heading/src/heading/index.ts @@ -0,0 +1,7 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './constants'; +export * from './createHeadingPlugin'; +export * from './types'; diff --git a/packages/heading/src/types.ts b/packages/heading/src/heading/types.ts similarity index 100% rename from packages/heading/src/types.ts rename to packages/heading/src/heading/types.ts diff --git a/packages/heading/src/index.ts b/packages/heading/src/index.ts index 2bdea46920..adec0a68b6 100644 --- a/packages/heading/src/index.ts +++ b/packages/heading/src/index.ts @@ -2,6 +2,6 @@ * @file Automatically generated by barrelsby. */ -export * from './constants'; -export * from './createHeadingPlugin'; -export * from './types'; +export * from './heading/index'; +export * from './toc/index'; +export * from './utils/index'; diff --git a/packages/heading/src/toc/createTocPlugin.ts b/packages/heading/src/toc/createTocPlugin.ts new file mode 100644 index 0000000000..4cca36aba6 --- /dev/null +++ b/packages/heading/src/toc/createTocPlugin.ts @@ -0,0 +1,9 @@ +import { createPluginFactory } from '@udecode/plate-common'; + +export const ELEMENT_TOC = 'toc'; + +export const createTocPlugin = createPluginFactory<{ continerRef: any }>({ + isElement: true, + isVoid: true, + key: ELEMENT_TOC, +}); diff --git a/packages/heading/src/toc/hooks/index.ts b/packages/heading/src/toc/hooks/index.ts new file mode 100644 index 0000000000..ce9dc1ab47 --- /dev/null +++ b/packages/heading/src/toc/hooks/index.ts @@ -0,0 +1,5 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './useTocElement'; diff --git a/packages/heading/src/toc/hooks/useTocElement.ts b/packages/heading/src/toc/hooks/useTocElement.ts new file mode 100644 index 0000000000..d91ce35624 --- /dev/null +++ b/packages/heading/src/toc/hooks/useTocElement.ts @@ -0,0 +1,89 @@ +import React, { useEffect } from 'react'; + +import { + addSelectedRow, + getNode, + toDOMNode, + useEditorRef, + useEditorSelector, +} from '@udecode/plate-common'; + +import type { HeadingList } from '../types'; + +import { getHeadingList, heightToTop } from '../../utils'; + +export type useTocElementStateProps = { + isScroll: boolean; + topOffset: number; +}; + +export const useTocElementState = ({ + isScroll, + topOffset, +}: useTocElementStateProps) => { + const headingList = useEditorSelector(getHeadingList, []); + const editor = useEditorRef(); + + const containerRef = React.useRef(null); + + useEffect(() => { + const container = toDOMNode(editor, editor); + + if (!container) return; + + containerRef.current = container; + + return () => { + containerRef.current = null; + }; + }, [editor]); + + const onContentScroll = React.useCallback( + (el: HTMLElement, id: string) => { + if (!containerRef.current) return; + if (isScroll) { + containerRef.current?.scrollTo({ + behavior: 'instant', + top: heightToTop(el, containerRef as any) - topOffset, + }); + } else { + const top = heightToTop(el) - topOffset; + window.scrollTo({ behavior: 'instant', top }); + } + + setTimeout(() => { + addSelectedRow(editor, id); + }, 0); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [isScroll, topOffset] + ); + + return { editor, headingList, onContentScroll }; +}; + +export const useTocElement = ({ + editor, + onContentScroll, +}: ReturnType) => { + return { + props: { + onClick: ( + e: React.MouseEvent, + item: HeadingList + ) => { + e.preventDefault(); + const { id, path } = item; + const node = getNode(editor, path); + + if (!node) return; + + const el = toDOMNode(editor, node); + + if (!el) return; + + onContentScroll(el, id); + }, + }, + }; +}; diff --git a/packages/heading/src/toc/index.ts b/packages/heading/src/toc/index.ts new file mode 100644 index 0000000000..ccccaf0f11 --- /dev/null +++ b/packages/heading/src/toc/index.ts @@ -0,0 +1,9 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './createTocPlugin'; +export * from './types'; +export * from './hooks/index'; +export * from './tocSideBar/index'; +export * from './transforms/index'; diff --git a/packages/heading/src/toc/tocSideBar/index.ts b/packages/heading/src/toc/tocSideBar/index.ts new file mode 100644 index 0000000000..4850900961 --- /dev/null +++ b/packages/heading/src/toc/tocSideBar/index.ts @@ -0,0 +1,7 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './useContentController'; +export * from './useTocController'; +export * from './useTocSideBarState'; diff --git a/packages/heading/src/toc/tocSideBar/useContentController.ts b/packages/heading/src/toc/tocSideBar/useContentController.ts new file mode 100644 index 0000000000..a776155d00 --- /dev/null +++ b/packages/heading/src/toc/tocSideBar/useContentController.ts @@ -0,0 +1,86 @@ +'use client'; + +import React from 'react'; + +import { addSelectedRow, useEditorRef } from '@udecode/plate-common'; + +import type { UseContentController } from '../types'; + +import { heightToTop } from '../../utils'; +import { useContentObserver } from './useContentObserver'; + +export const useContentController = ({ + containerRef, + isObserve, + rootMargin, + topOffset, +}: UseContentController) => { + const editor = useEditorRef(); + const [editorContentRef, setEditorContentRef] = React.useState(containerRef); + + const isScroll = + (editorContentRef.current?.scrollHeight || 0) > + (editorContentRef.current?.clientHeight || 0); + + const scrollContainer = React.useMemo(() => { + if (typeof window !== 'object') return; + + return isScroll ? editorContentRef.current : window; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isScroll]); + + const [status, setStatus] = React.useState(0); + + const { activeId } = useContentObserver({ + editorContentRef, + isObserve, + isScroll, + rootMargin, + status, + }); + + const [activeContentId, setActiveContentId] = React.useState(activeId); + + const onContentScroll = (el: HTMLElement, id: string) => { + setActiveContentId(id); + + if (isScroll) { + editorContentRef.current?.scrollTo({ + behavior: 'instant', + top: heightToTop(el, editorContentRef) - topOffset, + }); + } else { + const top = heightToTop(el) - topOffset; + // Note: if behavior === smooth,scrolling the toc then click the title immediately will scroll to the wrong position.It should be a chrome bug. + window.scrollTo({ behavior: 'instant', top }); + } + + addSelectedRow(editor, id); + }; + + React.useEffect(() => { + setEditorContentRef(containerRef); + }, [containerRef]); + + React.useEffect(() => { + setActiveContentId(activeId); + }, [activeId]); + + React.useEffect(() => { + if (!scrollContainer) return; + + const scroll = () => { + if (isObserve) { + setStatus(Date.now()); + } + }; + + scrollContainer.addEventListener('scroll', scroll); + + return () => { + scrollContainer.removeEventListener('scroll', scroll); + }; + }, [isObserve, scrollContainer]); + + return { activeContentId, onContentScroll }; +}; diff --git a/packages/heading/src/toc/tocSideBar/useContentObserver.ts b/packages/heading/src/toc/tocSideBar/useContentObserver.ts new file mode 100644 index 0000000000..b7ac0dd780 --- /dev/null +++ b/packages/heading/src/toc/tocSideBar/useContentObserver.ts @@ -0,0 +1,81 @@ +import React from 'react'; + +import { + getNode, + toDOMNode, + useEditorRef, + useEditorSelector, +} from '@udecode/plate-common'; + +import { getHeadingList } from '../../utils'; + +interface UseContentObserver { + editorContentRef: React.RefObject; + isObserve: boolean; + isScroll: boolean; + rootMargin: string; + status: number; +} + +export const useContentObserver = ({ + editorContentRef, + isObserve, + isScroll, + rootMargin, + status, +}: UseContentObserver) => { + const headingElementsRef = React.useRef< + Record + >({}); + + const root = isScroll ? editorContentRef.current : undefined; + const editor = useEditorRef(); + const headingList = useEditorSelector(getHeadingList, []); + + const [activeId, setActiveId] = React.useState(''); + + React.useEffect(() => { + const callback = (headings: IntersectionObserverEntry[]) => { + if (!isObserve) return; + + headingElementsRef.current = headings.reduce((map, headingElement) => { + map[headingElement.target.id] = headingElement; + + return map; + }, headingElementsRef.current); + + const visibleHeadings: string[] = []; + + Object.keys(headingElementsRef.current).forEach((key) => { + const headingElement = headingElementsRef.current[key]; + + if (headingElement.isIntersecting) visibleHeadings.push(key); + }); + const lastKey = Object.keys(headingElementsRef.current).pop()!; + visibleHeadings.length > 0 && setActiveId(visibleHeadings[0] || lastKey); + headingElementsRef.current = {}; + }; + const observer = new IntersectionObserver(callback, { + root: root, + rootMargin: rootMargin, + }); + + headingList.forEach((item) => { + const { path } = item; + + const node = getNode(editor, path); + + if (!node) return; + + const element = toDOMNode(editor, node); + + return element && observer.observe(element); + }); + + return () => { + observer.disconnect(); + }; + }, [headingList, isObserve, editor, root, rootMargin, status]); + + return { activeId }; +}; diff --git a/packages/heading/src/toc/tocSideBar/useTocController.ts b/packages/heading/src/toc/tocSideBar/useTocController.ts new file mode 100644 index 0000000000..c20b83e8ec --- /dev/null +++ b/packages/heading/src/toc/tocSideBar/useTocController.ts @@ -0,0 +1,38 @@ +import React from 'react'; + +import { useTocObserver } from './useTocObserver'; + +interface UseTocController { + activeId: string; + isObserve: boolean; + showHeader: boolean; + tocRef: React.RefObject; +} + +export const useTocController = ({ + activeId, + isObserve, + showHeader, + tocRef, +}: UseTocController) => { + const [activeTocId, setActiveTocId] = React.useState(''); + const { offset, visible } = useTocObserver({ + activeId: activeTocId, + isObserve, + showHeader, + tocRef, + }); + + React.useEffect(() => { + if (!visible) { + const tocItemWrapper = tocRef.current?.querySelector('#toc_wrap'); + const top = (tocItemWrapper?.scrollTop as any) + offset; + + tocItemWrapper?.scrollTo({ behavior: 'instant', top: top }); + } + }, [visible, offset, tocRef]); + + React.useEffect(() => { + setActiveTocId(activeId); + }, [activeId]); +}; diff --git a/packages/heading/src/toc/tocSideBar/useTocObserver.ts b/packages/heading/src/toc/tocSideBar/useTocObserver.ts new file mode 100644 index 0000000000..3708d9703c --- /dev/null +++ b/packages/heading/src/toc/tocSideBar/useTocObserver.ts @@ -0,0 +1,62 @@ +import React from 'react'; + +interface UseTocObserver { + activeId: string; + isObserve: boolean; + showHeader: boolean; + tocRef: React.RefObject; +} + +export const useTocObserver = ({ + activeId, + isObserve, + showHeader, + tocRef, +}: UseTocObserver) => { + const root = tocRef.current; + + const [visible, setVisible] = React.useState(true); + const [offset, setOffset] = React.useState(0); + + React.useEffect(() => { + const callback = (entries: IntersectionObserverEntry[]) => { + if (!isObserve) return; + + const entry = entries[0]; + const isBelow = + entry.boundingClientRect.bottom > entry.rootBounds!.bottom!; + + const isAbove = entry.boundingClientRect.top < entry.rootBounds!.top!; + + const isVisible = entry.intersectionRatio === 1; + + if (isVisible) { + setVisible(true); + } else { + setVisible(false); + + if (isAbove) { + setOffset(entry.boundingClientRect.top - entry.rootBounds!.top!); + } + if (isBelow) { + setOffset( + entry.boundingClientRect.bottom - entry.rootBounds!.bottom! + ); + } + } + }; + const observer = new IntersectionObserver(callback, { + root: root, + }); + + const element = document.querySelectorAll('#toc_item_active')[0]; + + if (element) observer.observe(element); + + return () => { + observer.disconnect(); + }; + }, [activeId, root, isObserve, showHeader]); + + return { offset, visible }; +}; diff --git a/packages/heading/src/toc/tocSideBar/useTocSideBarState.ts b/packages/heading/src/toc/tocSideBar/useTocSideBarState.ts new file mode 100644 index 0000000000..dead8ef6a2 --- /dev/null +++ b/packages/heading/src/toc/tocSideBar/useTocSideBarState.ts @@ -0,0 +1,119 @@ +import React from 'react'; + +import { + getNode, + toDOMNode, + useEditorRef, + useEditorSelector, +} from '@udecode/plate-common'; + +import type { HeadingList, TocSideBarProps } from '../types'; + +import { useContentController, useTocController } from '.'; +import { checkIn, getHeadingList } from '../../utils'; + +export const useTocSideBarState = ({ + containerRef, + onOpenChange, + open = true, + rootMargin = '0px 0px 0px 0px', + showHeader = true, + style, + topOffset = 0, +}: TocSideBarProps) => { + const editor = useEditorRef(); + const headingList = useEditorSelector(getHeadingList, []); + + const tocRef = React.useRef(null); + + const [mouseInToc, setMouseInToc] = React.useState(false); + + const [isObserve, setIsObserve] = React.useState(open); + + const { activeContentId, onContentScroll } = useContentController({ + containerRef, + isObserve, + rootMargin, + topOffset, + }); + + useTocController({ + activeId: activeContentId, + isObserve, + showHeader, + tocRef, + }); + + return { + activeContentId, + editor, + headingList, + mouseInToc, + onContentScroll, + onOpenChange, + open, + setIsObserve, + setMouseInToc, + showHeader, + style, + tocRef, + }; +}; + +export const useTocSideBar = ({ + editor, + mouseInToc, + onContentScroll, + open, + setIsObserve, + setMouseInToc, + tocRef, +}: ReturnType) => { + React.useEffect(() => { + if (mouseInToc) { + setIsObserve(false); + } else { + setIsObserve(true); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [mouseInToc]); + + const onConentClick = React.useCallback( + ( + e: React.MouseEvent, + item: HeadingList + ) => { + e.preventDefault(); + const { id, path } = item; + const node = getNode(editor, path); + + if (!node) return; + + const el = toDOMNode(editor, node); + + if (!el) return; + + onContentScroll(el, id); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [] + ); + + return { + navProps: { + onMouseEnter: () => { + !mouseInToc && open && setMouseInToc(true); + }, + onMouseLeave: ( + e: React.MouseEvent + ) => { + if (open) { + const isIn = checkIn(e); + isIn !== mouseInToc && setMouseInToc(isIn); + } + }, + ref: tocRef, + }, + onConentClick, + }; +}; diff --git a/packages/heading/src/toc/transforms/index.ts b/packages/heading/src/toc/transforms/index.ts new file mode 100644 index 0000000000..3a861f86be --- /dev/null +++ b/packages/heading/src/toc/transforms/index.ts @@ -0,0 +1,5 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './insertToc'; diff --git a/packages/heading/src/toc/transforms/insertToc.ts b/packages/heading/src/toc/transforms/insertToc.ts new file mode 100644 index 0000000000..e849a2308b --- /dev/null +++ b/packages/heading/src/toc/transforms/insertToc.ts @@ -0,0 +1,23 @@ +import { + type InsertNodesOptions, + type PlateEditor, + type TElement, + type Value, + insertNodes, +} from '@udecode/plate-common'; + +import { ELEMENT_TOC } from '../createTocPlugin'; + +export const insertToc = ( + editor: PlateEditor, + options?: InsertNodesOptions +) => { + insertNodes( + editor, + { + children: [{ text: '' }], + type: ELEMENT_TOC, + }, + options as any + ); +}; diff --git a/packages/heading/src/toc/types.ts b/packages/heading/src/toc/types.ts new file mode 100644 index 0000000000..860f751cbe --- /dev/null +++ b/packages/heading/src/toc/types.ts @@ -0,0 +1,27 @@ +import type { Path } from 'slate'; + +export interface HeadingList { + id: string; + path: Path; + title: string; + type: string; + depth?: number; +} + +export interface TocSideBarProps { + containerRef: React.RefObject; + className?: string; + onOpenChange?: (open: boolean) => void; + open?: boolean; + rootMargin?: string; + showHeader?: boolean; + style?: React.CSSProperties; + topOffset?: number; +} + +export interface UseContentController { + containerRef: React.RefObject; + isObserve: boolean; + rootMargin: string; + topOffset: number; +} diff --git a/packages/heading/src/utils/checkIn.ts b/packages/heading/src/utils/checkIn.ts new file mode 100644 index 0000000000..9201f183db --- /dev/null +++ b/packages/heading/src/utils/checkIn.ts @@ -0,0 +1,27 @@ +export function checkIn( + e: React.MouseEvent +) { + const event = window.event as unknown as React.MouseEvent< + HTMLElement, + globalThis.MouseEvent + >; + const x = Number(event.clientX); // 鼠标相对屏幕横坐标 + const y = Number(event.clientY); // 鼠标相对屏幕纵坐标 + + const ele = e.target as HTMLElement; + const div_x = Number(ele.getBoundingClientRect().left); // 相对屏幕的横坐标 + const div_x_width = Number( + ele.getBoundingClientRect().left + ele.clientWidth + ); // 相对屏幕的横坐标+width + + const div_y = Number(ele.getBoundingClientRect().top); // 相对屏幕的纵坐标 + const div_y_height = Number( + ele.getBoundingClientRect().top + ele.clientHeight + ); // 相对屏幕的纵坐标+height + + if (x > div_x && x < div_x_width && y > div_y && y < div_y_height) { + return true; + } + + return false; +} diff --git a/packages/heading/src/utils/getHeadingList.ts b/packages/heading/src/utils/getHeadingList.ts new file mode 100644 index 0000000000..3a3cd0c829 --- /dev/null +++ b/packages/heading/src/utils/getHeadingList.ts @@ -0,0 +1,48 @@ +import { + type PlateEditor, + type TElement, + getNodeEntries, + getNodeString, +} from '@udecode/plate-common'; + +import type { HeadingList } from '../toc'; + +import { + ELEMENT_H1, + ELEMENT_H2, + ELEMENT_H3, + ELEMENT_H4, + ELEMENT_H5, + ELEMENT_H6, +} from '../heading'; +import { isHeading } from './isHeading'; + +const headingDepth: Record = { + [ELEMENT_H1]: 1, + [ELEMENT_H2]: 2, + [ELEMENT_H3]: 3, + [ELEMENT_H4]: 4, + [ELEMENT_H5]: 5, + [ELEMENT_H6]: 6, +}; + +export const getHeadingList = (editor: PlateEditor) => { + const headingList: HeadingList[] = []; + + const values = getNodeEntries(editor, { + at: [], + match: (n) => isHeading(n), + }); + + if (!values) return []; + + Array.from(values, ([node, path]) => { + const { type } = node as TElement; + const title = getNodeString(node); + const depth = headingDepth[type]; + const id = node.id; + title && headingList.push({ depth, id, path, title, type }); + }); + + return headingList; +}; diff --git a/packages/heading/src/utils/heightToTop.ts b/packages/heading/src/utils/heightToTop.ts new file mode 100644 index 0000000000..25203e04bd --- /dev/null +++ b/packages/heading/src/utils/heightToTop.ts @@ -0,0 +1,18 @@ +import type React from 'react'; + +export const heightToTop = ( + ele: Element | HTMLElement, + editorContentRef?: React.RefObject +) => { + // ele为指定跳转到该位置的DOM节点 + const root = editorContentRef ? editorContentRef.current : document.body; + let height = 0; + + do { + height += (ele as HTMLElement).offsetTop; + // eslint-disable-next-line no-param-reassign + ele = (ele as HTMLElement).offsetParent as unknown as Element; + } while (ele !== root); + + return height; +}; diff --git a/packages/heading/src/utils/index.ts b/packages/heading/src/utils/index.ts new file mode 100644 index 0000000000..54b30bbde9 --- /dev/null +++ b/packages/heading/src/utils/index.ts @@ -0,0 +1,8 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './checkIn'; +export * from './heightToTop'; +export * from './isHeading'; +export * from './getHeadingList'; diff --git a/packages/heading/src/utils/isHeading.ts b/packages/heading/src/utils/isHeading.ts new file mode 100644 index 0000000000..f97583bb17 --- /dev/null +++ b/packages/heading/src/utils/isHeading.ts @@ -0,0 +1,7 @@ +import type { ENode, Value } from '@udecode/plate-common'; + +import { KEYS_HEADING } from '../heading'; + +export const isHeading = (node: ENode) => { + return node.type && KEYS_HEADING.includes(node.type as string); +}; diff --git a/packages/indent-list/src/transforms/indentList.ts b/packages/indent-list/src/transforms/indentList.ts index 1436196a0d..36355b238d 100644 --- a/packages/indent-list/src/transforms/indentList.ts +++ b/packages/indent-list/src/transforms/indentList.ts @@ -1,4 +1,5 @@ import type { PlateEditor, Value } from '@udecode/plate-common/server'; +import type { Location } from 'slate'; import { type SetIndentOptions, setIndent } from '@udecode/plate-indent'; @@ -10,6 +11,7 @@ import { ListStyleType } from '../types'; export interface IndentListOptions extends SetIndentOptions { + at?: Location; listStyleType?: ListStyleType | string; } diff --git a/packages/indent-list/src/transforms/index.ts b/packages/indent-list/src/transforms/index.ts index 3a20d3e668..e2aa556abe 100644 --- a/packages/indent-list/src/transforms/index.ts +++ b/packages/indent-list/src/transforms/index.ts @@ -8,5 +8,6 @@ export * from './setIndentListNode'; export * from './setIndentListNodes'; export * from './setIndentListSiblingNodes'; export * from './toggleIndentList'; +export * from './toggleIndentListByPath'; export * from './toggleIndentListSet'; export * from './toggleIndentListUnset'; diff --git a/packages/indent-list/src/transforms/toggleIndentListByPath.ts b/packages/indent-list/src/transforms/toggleIndentListByPath.ts new file mode 100644 index 0000000000..13f7f89755 --- /dev/null +++ b/packages/indent-list/src/transforms/toggleIndentListByPath.ts @@ -0,0 +1,42 @@ +import { + ELEMENT_DEFAULT, + type PlateEditor, + type TNodeEntry, + type Value, + setNodes, + unsetNodes, +} from '@udecode/plate-common'; +import { KEY_INDENT } from '@udecode/plate-indent'; + +import { + KEY_LIST_CHECKED, + KEY_LIST_STYLE_TYPE, +} from '../createIndentListPlugin'; + +export const toggleIndentListByPath = ( + editor: PlateEditor, + [node, path]: TNodeEntry, + listStyleType: string +) => { + setNodes( + editor, + { + [KEY_INDENT]: node.indent ?? 1, + // TODO: normalized if not todo remove this property. + [KEY_LIST_CHECKED]: false, + [KEY_LIST_STYLE_TYPE]: listStyleType, + type: ELEMENT_DEFAULT, + }, + { + at: path, + } + ); +}; + +export const toggleIndentListByPathUnSet = ( + editor: PlateEditor, + [, path]: TNodeEntry +) => + unsetNodes(editor, [KEY_LIST_STYLE_TYPE, KEY_INDENT, KEY_LIST_CHECKED], { + at: path, + }); diff --git a/packages/layout/src/onKeyDownColumn.ts b/packages/layout/src/onKeyDownColumn.ts index 795cc67b40..f8574cf132 100644 --- a/packages/layout/src/onKeyDownColumn.ts +++ b/packages/layout/src/onKeyDownColumn.ts @@ -2,10 +2,15 @@ import { type KeyboardHandlerReturnType, type PlateEditor, type Value, - getParentNode, + getAboveNode, + getAncestorNode, isHotkey, + isSelectionCoverBlock, select, } from '@udecode/plate-common/server'; +import { Path } from 'slate'; + +import { ELEMENT_COLUMN_GROUP } from './createColumnPlugin'; export const onKeyDownColumn = = PlateEditor>( @@ -17,14 +22,26 @@ export const onKeyDownColumn = const at = editor.selection; if (isHotkey('mod+a', e) && at) { - const selectionParent = getParentNode(editor, at); + const aboveNode = getAboveNode(editor); + const ancestorNode = getAncestorNode(editor); + + if (!ancestorNode) return; + if (!aboveNode) return; + + const [node] = ancestorNode; + + if (node.type !== ELEMENT_COLUMN_GROUP) return; + + const [, abovePath] = aboveNode; - if (!selectionParent) return; + let targetPath = Path.parent(abovePath); - const [, parentPath] = selectionParent; - parentPath.pop(); + if (isSelectionCoverBlock(editor)) { + targetPath = Path.parent(targetPath); + } + if (targetPath.length === 0) return; - select(editor, parentPath); + select(editor, targetPath); e.preventDefault(); e.stopPropagation(); diff --git a/packages/layout/src/transforms/index.ts b/packages/layout/src/transforms/index.ts index 4431b96ccb..cde427139e 100644 --- a/packages/layout/src/transforms/index.ts +++ b/packages/layout/src/transforms/index.ts @@ -6,3 +6,4 @@ export * from './insertColumnGroup'; export * from './insertEmptyColumn'; export * from './moveMiddleColumn'; export * from './setColumnWidth'; +export * from './toggleColumns'; diff --git a/packages/layout/src/transforms/toggleColumns.ts b/packages/layout/src/transforms/toggleColumns.ts new file mode 100644 index 0000000000..ef906ae40b --- /dev/null +++ b/packages/layout/src/transforms/toggleColumns.ts @@ -0,0 +1,41 @@ +import { + type EElement, + type ENode, + type PlateEditor, + type TNodeEntry, + type Value, + replaceNode, +} from '@udecode/plate-common'; + +import { ELEMENT_COLUMN, ELEMENT_COLUMN_GROUP } from '../createColumnPlugin'; + +export const toggleColumns = ( + editor: PlateEditor, + nodeEntry: TNodeEntry> +) => { + const nodes = { + children: [ + { + children: [nodeEntry[0]], + type: ELEMENT_COLUMN, + width: '33.33%', + }, + { + children: [{ text: '' }], + type: ELEMENT_COLUMN, + width: '33.33%', + }, + { + children: [{ text: '' }], + type: ELEMENT_COLUMN, + width: '33.33%', + }, + ], + type: ELEMENT_COLUMN_GROUP, + }; + + replaceNode(editor, { + at: nodeEntry[1], + nodes: nodes as EElement, + }); +}; diff --git a/packages/link/src/components/useLinkToolbarButton.ts b/packages/link/src/components/useLinkToolbarButton.ts index 9d19faaeb5..be0acf19a8 100644 --- a/packages/link/src/components/useLinkToolbarButton.ts +++ b/packages/link/src/components/useLinkToolbarButton.ts @@ -28,6 +28,9 @@ export const useLinkToolbarButton = ( onClick: () => { triggerFloatingLink(editor, { focused: true }); }, + onMouseDown: (e: React.MouseEvent) => { + e.preventDefault(); + }, pressed: state.pressed, }, }; diff --git a/packages/math/.npmignore b/packages/math/.npmignore new file mode 100644 index 0000000000..7d3b305b17 --- /dev/null +++ b/packages/math/.npmignore @@ -0,0 +1,3 @@ +__tests__ +__test-utils__ +__mocks__ diff --git a/packages/math/README.md b/packages/math/README.md new file mode 100644 index 0000000000..bd4ca49cbf --- /dev/null +++ b/packages/math/README.md @@ -0,0 +1,11 @@ +# Plate math plugin + +This package implements the math plugin for Plate. + +## Documentation + +Check out [math](https://platejs.org/docs/math). + +## License + +[MIT](../../LICENSE) diff --git a/packages/math/package.json b/packages/math/package.json new file mode 100644 index 0000000000..81e55b602b --- /dev/null +++ b/packages/math/package.json @@ -0,0 +1,65 @@ +{ + "name": "@udecode/plate-math", + "version": "33.0.3", + "description": "Math plugin for Plate", + "license": "MIT", + "homepage": "https://platejs.org", + "repository": { + "type": "git", + "url": "https://github.com/udecode/plate.git", + "directory": "packages/math" + }, + "bugs": { + "url": "https://github.com/udecode/plate/issues" + }, + "sideEffects": false, + "main": "dist/index.js", + "module": "dist/index.mjs", + "types": "dist/index.d.ts", + "files": [ + "dist/**/*" + ], + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "module": "./dist/index.mjs", + "require": "./dist/index.js" + } + }, + "scripts": { + "build": "yarn p:build", + "build:watch": "yarn p:build:watch", + "brl": "yarn p:brl", + "clean": "yarn p:clean", + "lint": "yarn p:lint", + "lint:fix": "yarn p:lint:fix", + "test": "yarn p:test", + "test:watch": "yarn p:test:watch", + "typecheck": "yarn p:typecheck" + }, + "dependencies": { + "katex": "0.16.10" + }, + "devDependencies": { + "@types/katex": "0.16.7", + "@udecode/plate-common": "workspace:^" + }, + "peerDependencies": { + "@udecode/plate-common": ">=33.0.4", + "react": ">=16.8.0", + "react-dom": ">=16.8.0", + "slate": ">=0.94.0", + "slate-history": ">=0.93.0", + "slate-hyperscript": ">=0.66.0", + "slate-react": ">=0.99.0" + }, + "keywords": [ + "plate", + "plugin", + "slate" + ], + "publishConfig": { + "access": "public" + } +} diff --git a/packages/math/src/equation/components/EquationButton.ts b/packages/math/src/equation/components/EquationButton.ts new file mode 100644 index 0000000000..44243c363f --- /dev/null +++ b/packages/math/src/equation/components/EquationButton.ts @@ -0,0 +1,27 @@ +import { + addSelectedRow, + useEditorRef, + useElement, +} from '@udecode/plate-common'; + +import type { TEquationElement } from '../types'; + +export const useEquationButtonState = () => { + const editor = useEditorRef(); + const element = useElement(); + + return { editor, element }; +}; + +export const useEquationButton = ({ + editor, + element, +}: ReturnType) => { + return { + props: { + onClick: () => { + addSelectedRow(editor, element.id as string); + }, + }, + }; +}; diff --git a/packages/math/src/equation/components/EquationInput.ts b/packages/math/src/equation/components/EquationInput.ts new file mode 100644 index 0000000000..0298d06ff6 --- /dev/null +++ b/packages/math/src/equation/components/EquationInput.ts @@ -0,0 +1,56 @@ +import React, { useEffect } from 'react'; + +import { + addSelectedRow, + isHotkey, + setNodes, + useEditorRef, + useElement, +} from '@udecode/plate-common'; + +import type { TEquationElement } from '../types'; + +export const useEquationInputState = ({ isOpen }: { isOpen: boolean }) => { + const editor = useEditorRef(); + const element = useElement(); + const inputRef = React.useRef(null); + + return { editor, element, inputRef, isOpen }; +}; + +export const useEquationInput = ({ + editor, + element, + inputRef, + isOpen, +}: ReturnType) => { + useEffect(() => { + if (isOpen) { + setTimeout(() => { + inputRef.current?.focus(); + }, 0); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isOpen]); + + return { + props: { + onChange: (e: React.ChangeEvent) => { + setNodes(editor, { + texExpression: e.target.value, + }); + }, + onKeyDown: (e: React.KeyboardEvent) => { + if (isHotkey('enter')(e) || isHotkey('escape')(e)) { + e.preventDefault(); + addSelectedRow(editor, element.id as string); + } + }, + onMouseEnter: (e: React.MouseEvent) => { + inputRef.current?.focus(); + }, + ref: inputRef, + value: element.texExpression, + }, + }; +}; diff --git a/packages/math/src/equation/components/index.ts b/packages/math/src/equation/components/index.ts new file mode 100644 index 0000000000..25c36d93ff --- /dev/null +++ b/packages/math/src/equation/components/index.ts @@ -0,0 +1,6 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './EquationButton'; +export * from './EquationInput'; diff --git a/packages/math/src/equation/createEquationPlugin.ts b/packages/math/src/equation/createEquationPlugin.ts new file mode 100644 index 0000000000..b66c6b58ad --- /dev/null +++ b/packages/math/src/equation/createEquationPlugin.ts @@ -0,0 +1,11 @@ +import { createPluginFactory } from '@udecode/plate-common/server'; + +import 'katex/dist/katex.min.css'; + +export const ELEMENT_EQUATION = 'equation'; + +export const createEquationPlugin = createPluginFactory({ + isElement: true, + isVoid: true, + key: ELEMENT_EQUATION, +}); diff --git a/packages/math/src/equation/hooks/index.ts b/packages/math/src/equation/hooks/index.ts new file mode 100644 index 0000000000..bf11721f42 --- /dev/null +++ b/packages/math/src/equation/hooks/index.ts @@ -0,0 +1,5 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './useEquationElement'; diff --git a/packages/math/src/equation/hooks/useEquationElement.ts b/packages/math/src/equation/hooks/useEquationElement.ts new file mode 100644 index 0000000000..35cc3b0e2e --- /dev/null +++ b/packages/math/src/equation/hooks/useEquationElement.ts @@ -0,0 +1,26 @@ +import React from 'react'; + +import katex, { type KatexOptions } from 'katex'; + +import type { TEquationElement } from '../types'; + +export interface useEquationState { + element: TEquationElement; + katexRef: React.MutableRefObject; + options?: KatexOptions; +} + +export const useEquationState = ({ + element, + katexRef, + options, +}: useEquationState) => { + React.useEffect(() => { + if (!katexRef.current) return; + + katex.render(element.texExpression, katexRef.current, options); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [element.texExpression]); +}; + +export const useEquationElement = () => {}; diff --git a/packages/math/src/equation/index.ts b/packages/math/src/equation/index.ts new file mode 100644 index 0000000000..f62a8cbc10 --- /dev/null +++ b/packages/math/src/equation/index.ts @@ -0,0 +1,9 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './createEquationPlugin'; +export * from './types'; +export * from './components/index'; +export * from './hooks/index'; +export * from './transforms/index'; diff --git a/packages/math/src/equation/transforms/index.ts b/packages/math/src/equation/transforms/index.ts new file mode 100644 index 0000000000..589be5ac95 --- /dev/null +++ b/packages/math/src/equation/transforms/index.ts @@ -0,0 +1,5 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './insertEquation'; diff --git a/packages/math/src/equation/transforms/insertEquation.ts b/packages/math/src/equation/transforms/insertEquation.ts new file mode 100644 index 0000000000..e345b9df02 --- /dev/null +++ b/packages/math/src/equation/transforms/insertEquation.ts @@ -0,0 +1,25 @@ +import { + type InsertNodesOptions, + type PlateEditor, + type Value, + insertNodes, +} from '@udecode/plate-common'; + +import type { TEquationElement } from '../types'; + +import { ELEMENT_EQUATION } from '../createEquationPlugin'; + +export const insertEquation = ( + editor: PlateEditor, + options?: InsertNodesOptions +) => { + insertNodes( + editor, + { + children: [{ text: '' }], + texExpression: '', + type: ELEMENT_EQUATION, + }, + options as any + ); +}; diff --git a/packages/math/src/equation/types.ts b/packages/math/src/equation/types.ts new file mode 100644 index 0000000000..e3f69275a8 --- /dev/null +++ b/packages/math/src/equation/types.ts @@ -0,0 +1,5 @@ +import type { TElement } from '@udecode/plate-common'; + +export interface TEquationElement extends TElement { + texExpression: string; +} diff --git a/packages/math/src/index.ts b/packages/math/src/index.ts new file mode 100644 index 0000000000..85082ac411 --- /dev/null +++ b/packages/math/src/index.ts @@ -0,0 +1,6 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './equation/index'; +export * from './inline-equation/index'; diff --git a/packages/math/src/inline-equation/components/InlineEquationButton.ts b/packages/math/src/inline-equation/components/InlineEquationButton.ts new file mode 100644 index 0000000000..e8512f0599 --- /dev/null +++ b/packages/math/src/inline-equation/components/InlineEquationButton.ts @@ -0,0 +1,34 @@ +import { useMemo } from 'react'; + +import { findNodePath, useEditorRef, useElement } from '@udecode/plate-common'; + +import type { TInlineEquationElement } from '../types'; + +import { setSelectionInlineEquation } from '../utils/setSelectionInlineEquation'; + +export const useInlineEquationButtonState = () => { + const editor = useEditorRef(); + const element = useElement(); + + const inlineEquationPath = useMemo( + () => findNodePath(editor, element), + // eslint-disable-next-line react-hooks/exhaustive-deps + [element] + ); + + return { editor, element, inlineEquationPath }; +}; + +export const useInlineEquationButton = ({ + editor, + inlineEquationPath, +}: ReturnType) => { + return { + props: { + onClick: () => { + inlineEquationPath && + setSelectionInlineEquation(editor, inlineEquationPath, 'right'); + }, + }, + }; +}; diff --git a/packages/math/src/inline-equation/components/InlineEquationInput.ts b/packages/math/src/inline-equation/components/InlineEquationInput.ts new file mode 100644 index 0000000000..306d443aba --- /dev/null +++ b/packages/math/src/inline-equation/components/InlineEquationInput.ts @@ -0,0 +1,121 @@ +import React, { useEffect } from 'react'; + +import { + findNodePath, + isHotkey, + setNodes, + useEditorRef, + useEditorSelector, + useElement, +} from '@udecode/plate-common'; + +import type { TInlineEquationElement } from '../types'; + +import { setSelectionInlineEquation } from '../utils/setSelectionInlineEquation'; + +export const useInlineEquationInputState = ({ + isOpen, +}: { + isOpen: boolean; +}) => { + const editor = useEditorRef(); + const element = useElement(); + const inputRef = React.useRef(null); + + // if user press escape revert + const [originExpression, setOriginExpressin] = React.useState( + null + ); + + const inlineEquationPath = useEditorSelector( + (editor) => findNodePath(editor, element), + [] + ); + + return { + editor, + element, + inlineEquationPath, + inputRef, + isOpen, + originExpression, + setOriginExpressin, + }; +}; + +export const useInlineEquationInput = ({ + editor, + element, + inlineEquationPath, + inputRef, + isOpen, + originExpression, + setOriginExpressin, +}: ReturnType) => { + useEffect(() => { + if (isOpen) { + setTimeout(() => { + inputRef.current?.focus(); + inputRef.current?.setSelectionRange(0, element.texExpression.length); + setOriginExpressin(element.texExpression); + }, 0); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isOpen]); + + return { + props: { + onChange: (e: React.ChangeEvent) => { + setNodes( + editor, + { + texExpression: e.target.value, + }, + { at: inlineEquationPath } + ); + }, + onKeyDown: (e: React.KeyboardEvent) => { + if (!inlineEquationPath) return; + + const input = e.target as HTMLInputElement; + const { selectionEnd, selectionStart } = input; + + // at the left edge + if ( + selectionStart === 0 && + selectionEnd === 0 && + isHotkey('ArrowLeft')(e) + ) { + setSelectionInlineEquation(editor, inlineEquationPath, 'left'); + } + // at the right edge + if ( + selectionEnd === input.value.length && + selectionStart === input.value.length && + isHotkey('ArrowRight')(e) + ) { + setSelectionInlineEquation(editor, inlineEquationPath, 'right'); + } + // pressingt the enter set to right edge + if (isHotkey('Enter')(e)) { + e.preventDefault(); + setSelectionInlineEquation(editor, inlineEquationPath, 'right'); + } + // revert to the originExpression when pressing the escape + if (isHotkey('escape')(e)) { + setNodes( + editor, + { + texExpression: originExpression, + }, + { at: inlineEquationPath } + ); + + setSelectionInlineEquation(editor, inlineEquationPath, 'right'); + } + }, + ref: inputRef, + value: element.texExpression, + }, + }; +}; diff --git a/packages/math/src/inline-equation/components/index.ts b/packages/math/src/inline-equation/components/index.ts new file mode 100644 index 0000000000..546c1371b3 --- /dev/null +++ b/packages/math/src/inline-equation/components/index.ts @@ -0,0 +1,6 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './InlineEquationButton'; +export * from './InlineEquationInput'; diff --git a/packages/math/src/inline-equation/createInlineEquationPlugin.ts b/packages/math/src/inline-equation/createInlineEquationPlugin.ts new file mode 100644 index 0000000000..eae095e8cc --- /dev/null +++ b/packages/math/src/inline-equation/createInlineEquationPlugin.ts @@ -0,0 +1,10 @@ +import { createPluginFactory } from '@udecode/plate-common/server'; + +export const ELEMENT_INLINE_EQUATION = 'inline_equation'; + +export const createInlineEquationPlugin = createPluginFactory({ + isElement: true, + isInline: true, + isVoid: true, + key: ELEMENT_INLINE_EQUATION, +}); diff --git a/packages/math/src/inline-equation/hooks/index.ts b/packages/math/src/inline-equation/hooks/index.ts new file mode 100644 index 0000000000..1d91f41ec7 --- /dev/null +++ b/packages/math/src/inline-equation/hooks/index.ts @@ -0,0 +1,5 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './useInlineEquationElement'; diff --git a/packages/math/src/inline-equation/hooks/useInlineEquationElement.ts b/packages/math/src/inline-equation/hooks/useInlineEquationElement.ts new file mode 100644 index 0000000000..1848380b23 --- /dev/null +++ b/packages/math/src/inline-equation/hooks/useInlineEquationElement.ts @@ -0,0 +1,26 @@ +import React from 'react'; + +import katex, { type KatexOptions } from 'katex'; + +import type { TInlineEquationElement } from '../types'; + +export interface useInlineEquationState { + element: TInlineEquationElement; + katexRef: React.MutableRefObject; + options?: KatexOptions; +} + +export const useInlineEquationState = ({ + element, + katexRef, + options, +}: useInlineEquationState) => { + React.useEffect(() => { + if (!katexRef.current) return; + + katex.render(element.texExpression, katexRef.current, options); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [element.texExpression]); +}; + +export const useInlineEquationElement = () => {}; diff --git a/packages/math/src/inline-equation/index.ts b/packages/math/src/inline-equation/index.ts new file mode 100644 index 0000000000..cad444bfc6 --- /dev/null +++ b/packages/math/src/inline-equation/index.ts @@ -0,0 +1,9 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './createInlineEquationPlugin'; +export * from './types'; +export * from './components/index'; +export * from './hooks/index'; +export * from './transforms/index'; diff --git a/packages/math/src/inline-equation/transforms/index.ts b/packages/math/src/inline-equation/transforms/index.ts new file mode 100644 index 0000000000..4e38c68b67 --- /dev/null +++ b/packages/math/src/inline-equation/transforms/index.ts @@ -0,0 +1,5 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './insertInlineEquation'; diff --git a/packages/math/src/inline-equation/transforms/insertInlineEquation.ts b/packages/math/src/inline-equation/transforms/insertInlineEquation.ts new file mode 100644 index 0000000000..d559c891a7 --- /dev/null +++ b/packages/math/src/inline-equation/transforms/insertInlineEquation.ts @@ -0,0 +1,25 @@ +import { + type InsertNodesOptions, + type PlateEditor, + type Value, + insertNodes, +} from '@udecode/plate-common'; + +import type { TInlineEquationElement } from '../types'; + +import { ELEMENT_INLINE_EQUATION } from '../createInlineEquationPlugin'; + +export const insertInlineEquation = ( + editor: PlateEditor, + options?: InsertNodesOptions +) => { + insertNodes( + editor, + { + children: [{ text: '' }], + texExpression: '', + type: ELEMENT_INLINE_EQUATION, + }, + options as any + ); +}; diff --git a/packages/math/src/inline-equation/types.ts b/packages/math/src/inline-equation/types.ts new file mode 100644 index 0000000000..4cb0852823 --- /dev/null +++ b/packages/math/src/inline-equation/types.ts @@ -0,0 +1,5 @@ +import type { TElement } from '@udecode/plate-common'; + +export interface TInlineEquationElement extends TElement { + texExpression: string; +} diff --git a/packages/math/src/inline-equation/utils/setSelectionInlineEquation.ts b/packages/math/src/inline-equation/utils/setSelectionInlineEquation.ts new file mode 100644 index 0000000000..a6b85b26d1 --- /dev/null +++ b/packages/math/src/inline-equation/utils/setSelectionInlineEquation.ts @@ -0,0 +1,31 @@ +import type { Path, Range } from 'slate'; + +import { + type PlateEditor, + type Value, + focusEditor, + getNextNodeStartPoint, + getPreviousNodeEndPoint, + setSelection, +} from '@udecode/plate-common'; + +export const setSelectionInlineEquation = ( + editor: PlateEditor, + at: Path, + direction: 'left' | 'right' +) => { + const point = + direction === 'left' + ? getPreviousNodeEndPoint(editor, at) + : getNextNodeStartPoint(editor, at); + + if (!point) return; + + const range: Range = { + anchor: point, + focus: point, + }; + + setSelection(editor, range); + focusEditor(editor); +}; diff --git a/packages/math/tsconfig.build.json b/packages/math/tsconfig.build.json new file mode 100644 index 0000000000..425481e027 --- /dev/null +++ b/packages/math/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "../../config/tsconfig.build.json", + "compilerOptions": { + "declarationDir": "./dist", + "outDir": "./dist" + }, + "include": ["src"] +} diff --git a/packages/math/tsconfig.json b/packages/math/tsconfig.json new file mode 100644 index 0000000000..ad83d092a5 --- /dev/null +++ b/packages/math/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../../config/tsconfig.base.json", + "include": ["src"], + "exclude": [] +} diff --git a/packages/media/src/audio/createAudioPlugin.ts b/packages/media/src/audio/createAudioPlugin.ts new file mode 100644 index 0000000000..026f742efd --- /dev/null +++ b/packages/media/src/audio/createAudioPlugin.ts @@ -0,0 +1,11 @@ +import { createPluginFactory } from '@udecode/plate-common'; + +import type { AudioPlugin } from './types'; + +export const ELEMENT_AUDIO = 'audio'; + +export const createAudioPlugin = createPluginFactory({ + isElement: true, + isVoid: true, + key: ELEMENT_AUDIO, +}); diff --git a/packages/media/src/audio/index.ts b/packages/media/src/audio/index.ts new file mode 100644 index 0000000000..1b03e25c6a --- /dev/null +++ b/packages/media/src/audio/index.ts @@ -0,0 +1,6 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './createAudioPlugin'; +export * from './types'; diff --git a/packages/media/src/audio/types.ts b/packages/media/src/audio/types.ts new file mode 100644 index 0000000000..519dc905e1 --- /dev/null +++ b/packages/media/src/audio/types.ts @@ -0,0 +1,5 @@ +import type { TMediaElement } from '../media'; + +export interface TAudioElement extends TMediaElement {} + +export interface AudioPlugin {} diff --git a/packages/media/src/file/createFilePlugin.ts b/packages/media/src/file/createFilePlugin.ts new file mode 100644 index 0000000000..c1baa5d0f6 --- /dev/null +++ b/packages/media/src/file/createFilePlugin.ts @@ -0,0 +1,11 @@ +import { createPluginFactory } from '@udecode/plate-common'; + +import type { FilePlugin } from './types'; + +export const ELEMENT_FILE = 'file'; + +export const createFilePlugin = createPluginFactory({ + isElement: true, + isVoid: true, + key: ELEMENT_FILE, +}); diff --git a/packages/media/src/file/index.ts b/packages/media/src/file/index.ts new file mode 100644 index 0000000000..801f3b1cb2 --- /dev/null +++ b/packages/media/src/file/index.ts @@ -0,0 +1,6 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './createFilePlugin'; +export * from './types'; diff --git a/packages/media/src/file/types.ts b/packages/media/src/file/types.ts new file mode 100644 index 0000000000..15cd671279 --- /dev/null +++ b/packages/media/src/file/types.ts @@ -0,0 +1,5 @@ +import type { TMediaElement } from '../media'; + +export interface TFileElement extends TMediaElement {} + +export interface FilePlugin {} diff --git a/packages/media/src/index.ts b/packages/media/src/index.ts index 80dbac1efb..fb9124526a 100644 --- a/packages/media/src/index.ts +++ b/packages/media/src/index.ts @@ -2,6 +2,10 @@ * @file Automatically generated by barrelsby. */ +export * from './audio/index'; +export * from './file/index'; export * from './image/index'; export * from './media/index'; export * from './media-embed/index'; +export * from './placeholder/index'; +export * from './video/index'; diff --git a/packages/media/src/media/types.ts b/packages/media/src/media/types.ts index e664675ccf..1aa93ce13d 100644 --- a/packages/media/src/media/types.ts +++ b/packages/media/src/media/types.ts @@ -3,6 +3,9 @@ import type { TElement } from '@udecode/plate-common'; export interface TMediaElement extends TElement { url: string; align?: 'center' | 'left' | 'right'; + id?: string; + isUpload?: boolean; + name?: string; } export interface MediaPlugin { diff --git a/packages/media/src/media/useMediaState.ts b/packages/media/src/media/useMediaState.ts index 83a0af1efb..aecf6a7324 100644 --- a/packages/media/src/media/useMediaState.ts +++ b/packages/media/src/media/useMediaState.ts @@ -6,6 +6,7 @@ import { useFocused, useReadOnly, useSelected } from 'slate-react'; import type { TMediaElement } from './types'; import { VIDEO_PROVIDERS } from '../media-embed'; +import { ELEMENT_VIDEO } from '../video'; export type EmbedUrlData = { id?: string; @@ -25,10 +26,10 @@ export const useMediaState = ({ const selected = useSelected(); const readOnly = useReadOnly(); - const { align, url } = element; + const { align = 'left', id, isUpload, name, type, url } = element; const embed = React.useMemo(() => { - if (!urlParsers) return; + if (!urlParsers || type !== ELEMENT_VIDEO) return; for (const parser of urlParsers) { const data = parser(url); @@ -37,6 +38,7 @@ export const useMediaState = ({ return data; } } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [urlParsers, url]); const isTweet = embed?.provider === 'twitter'; @@ -47,10 +49,14 @@ export const useMediaState = ({ align, embed, focused, + id, isTweet, + isUpload, isVideo, isYoutube, + name, readOnly, selected, + url, }; }; diff --git a/packages/media/src/placeholder/createPlaceholderPlugin.ts b/packages/media/src/placeholder/createPlaceholderPlugin.ts new file mode 100644 index 0000000000..2be14e56e6 --- /dev/null +++ b/packages/media/src/placeholder/createPlaceholderPlugin.ts @@ -0,0 +1,22 @@ +import { + blurEditor, + createPluginFactory, + isHotkey, +} from '@udecode/plate-common'; + +import type { MediaPlaceholder } from './types'; + +export const ELEMENT_PLACEHOLDER = 'placeholder'; + +export const createPlaceholderPlugin = createPluginFactory({ + handlers: { + onKeyDown: (editor) => (event) => { + if (isHotkey('escape')(event)) { + blurEditor(editor); + } + }, + }, + isElement: true, + isVoid: true, + key: ELEMENT_PLACEHOLDER, +}); diff --git a/packages/media/src/placeholder/hooks/index.ts b/packages/media/src/placeholder/hooks/index.ts new file mode 100644 index 0000000000..d1c6003570 --- /dev/null +++ b/packages/media/src/placeholder/hooks/index.ts @@ -0,0 +1,5 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './usePlaceholderElement'; diff --git a/packages/media/src/placeholder/hooks/usePlaceholderElement.ts b/packages/media/src/placeholder/hooks/usePlaceholderElement.ts new file mode 100644 index 0000000000..25cddacd62 --- /dev/null +++ b/packages/media/src/placeholder/hooks/usePlaceholderElement.ts @@ -0,0 +1,36 @@ +import { useEditorRef, useElement } from '@udecode/plate-common'; +import { useFocused, useReadOnly, useSelected } from 'slate-react'; + +import type { TPlaceholderElement } from '../types'; + +import { ELEMENT_PLACEHOLDER } from '../createPlaceholderPlugin'; +import { usePlaceholderStore } from '../placeholderStore'; + +export const usePlaceholderElementState = () => { + const element = useElement(); + const editor = useEditorRef(); + const focused = useFocused(); + const readOnly = useReadOnly(); + const selected = useSelected(); + + const progresses = usePlaceholderStore().get.progresses(); + const isUploading = usePlaceholderStore().get.isUploading(); + const updatedFiles = usePlaceholderStore().get.updatedFiles(); + + const { mediaType } = useElement(ELEMENT_PLACEHOLDER); + + const progressing = updatedFiles.length > 0 && isUploading; + + return { + editor, + element, + focused, + isUploading, + mediaType, + progresses, + progressing, + readOnly, + selected, + updatedFiles, + }; +}; diff --git a/packages/media/src/placeholder/index.ts b/packages/media/src/placeholder/index.ts new file mode 100644 index 0000000000..e8a3de54c6 --- /dev/null +++ b/packages/media/src/placeholder/index.ts @@ -0,0 +1,10 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './createPlaceholderPlugin'; +export * from './placeholderStore'; +export * from './types'; +export * from './hooks/index'; +export * from './popover/index'; +export * from './transforms/index'; diff --git a/packages/media/src/placeholder/placeholderStore.ts b/packages/media/src/placeholder/placeholderStore.ts new file mode 100644 index 0000000000..c42897a1ee --- /dev/null +++ b/packages/media/src/placeholder/placeholderStore.ts @@ -0,0 +1,18 @@ +import { createAtomStore } from '@udecode/plate-common'; + +type progresses = Record; +interface placeholderStore { + isUploading: boolean; + progresses: progresses; + updatedFiles: File[]; +} + +export const { PlaceholderProvider, placeholderStore, usePlaceholderStore } = + createAtomStore( + { + isUploading: false, + progresses: {}, + updatedFiles: [], + } as placeholderStore, + { name: 'placeholder' } + ); diff --git a/packages/media/src/placeholder/popover/index.ts b/packages/media/src/placeholder/popover/index.ts new file mode 100644 index 0000000000..cbad17ac9e --- /dev/null +++ b/packages/media/src/placeholder/popover/index.ts @@ -0,0 +1,5 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './usePlaceholderPopover'; diff --git a/packages/media/src/placeholder/popover/usePlaceholderPopover.ts b/packages/media/src/placeholder/popover/usePlaceholderPopover.ts new file mode 100644 index 0000000000..3c3fca7bde --- /dev/null +++ b/packages/media/src/placeholder/popover/usePlaceholderPopover.ts @@ -0,0 +1,46 @@ +import { + isSelectionExpanded, + useEditorRef, + useEditorSelector, + useElement, +} from '@udecode/plate-common'; +import { useFocused, useReadOnly, useSelected } from 'slate-react'; + +import type { TPlaceholderElement } from '../types'; + +import { ELEMENT_PLACEHOLDER } from '../createPlaceholderPlugin'; +import { usePlaceholderStore } from '../placeholderStore'; + +export const usePlaceholderPopoverState = () => { + const editor = useEditorRef(); + const readOnly = useReadOnly(); + const selected = useSelected(); + const focused = useFocused(); + + const selectionCollapsed = useEditorSelector( + // eslint-disable-next-line @typescript-eslint/no-shadow + (editor) => !isSelectionExpanded(editor), + [] + ); + + const element = useElement(ELEMENT_PLACEHOLDER); + const { id, mediaType } = element; + + const setProgresses = usePlaceholderStore().set.progresses(); + const setIsUploading = usePlaceholderStore().set.isUploading(); + const setUpdatedFiles = usePlaceholderStore().set.updatedFiles(); + + return { + editor, + element, + focused, + id, + mediaType, + readOnly, + selected, + selectionCollapsed, + setIsUploading, + setProgresses, + setUpdatedFiles, + }; +}; diff --git a/packages/media/src/placeholder/transforms/index.ts b/packages/media/src/placeholder/transforms/index.ts new file mode 100644 index 0000000000..626b50e902 --- /dev/null +++ b/packages/media/src/placeholder/transforms/index.ts @@ -0,0 +1,6 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './insertPlaceholder'; +export * from './setMediaNode'; diff --git a/packages/media/src/placeholder/transforms/insertPlaceholder.ts b/packages/media/src/placeholder/transforms/insertPlaceholder.ts new file mode 100644 index 0000000000..7fd4911efa --- /dev/null +++ b/packages/media/src/placeholder/transforms/insertPlaceholder.ts @@ -0,0 +1,35 @@ +import { + type InsertNodesOptions, + type PlateEditor, + type Value, + insertNodes, + withoutNormalizing, +} from '@udecode/plate-common'; + +import type { TPlaceholderElement } from '../types'; + +import { ELEMENT_IMAGE } from '../../image'; +import { ELEMENT_PLACEHOLDER } from '../createPlaceholderPlugin'; + +export const insertPlaceHolder = ( + editor: PlateEditor, + mediaType: string, + options?: InsertNodesOptions +) => { + withoutNormalizing(editor, () => + insertNodes( + editor, + { + children: [{ text: '' }], + mediaType, + type: ELEMENT_PLACEHOLDER, + }, + options as any + ) + ); +}; + +export const insertImagePlaceholder = ( + editor: PlateEditor, + options?: InsertNodesOptions +) => insertPlaceHolder(editor, ELEMENT_IMAGE, options); diff --git a/packages/media/src/placeholder/transforms/setMediaNode.ts b/packages/media/src/placeholder/transforms/setMediaNode.ts new file mode 100644 index 0000000000..ba448a9cf3 --- /dev/null +++ b/packages/media/src/placeholder/transforms/setMediaNode.ts @@ -0,0 +1,19 @@ +import { + type PlateEditor, + type SetNodesOptions, + setNodes, +} from '@udecode/plate-common'; + +type props = { + isUpload?: boolean; + name?: string; + type: string; + url: string; + width?: number; +}; + +export const setMediaNode = ( + editor: PlateEditor, + props: props, + options?: SetNodesOptions +) => setNodes(editor, props, options); diff --git a/packages/media/src/placeholder/types.ts b/packages/media/src/placeholder/types.ts new file mode 100644 index 0000000000..371bb7bdec --- /dev/null +++ b/packages/media/src/placeholder/types.ts @@ -0,0 +1,13 @@ +import type { TElement } from '@udecode/plate-common'; + +export interface TPlaceholderElement extends TElement { + mediaType: string; +} + +export interface placeholderRule { + mediaType: string; +} + +export interface MediaPlaceholder { + rules?: placeholderRule[]; +} diff --git a/packages/media/src/video/createVideoPlugin.ts b/packages/media/src/video/createVideoPlugin.ts new file mode 100644 index 0000000000..8461d0f651 --- /dev/null +++ b/packages/media/src/video/createVideoPlugin.ts @@ -0,0 +1,11 @@ +import { createPluginFactory } from '@udecode/plate-common'; + +import type { VideoPlugin } from './types'; + +export const ELEMENT_VIDEO = 'video'; + +export const createVideoPlugin = createPluginFactory({ + isElement: true, + isVoid: true, + key: ELEMENT_VIDEO, +}); diff --git a/packages/media/src/video/index.ts b/packages/media/src/video/index.ts new file mode 100644 index 0000000000..4c42141058 --- /dev/null +++ b/packages/media/src/video/index.ts @@ -0,0 +1,6 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './createVideoPlugin'; +export * from './types'; diff --git a/packages/media/src/video/types.ts b/packages/media/src/video/types.ts new file mode 100644 index 0000000000..f32501426a --- /dev/null +++ b/packages/media/src/video/types.ts @@ -0,0 +1,5 @@ +import type { TMediaElement } from '../media'; + +export interface TVideoElement extends TMediaElement {} + +export interface VideoPlugin {} diff --git a/packages/plate-utils/src/shared/addSelectedRow.ts b/packages/plate-utils/src/shared/addSelectedRow.ts new file mode 100644 index 0000000000..a1d1b9a236 --- /dev/null +++ b/packages/plate-utils/src/shared/addSelectedRow.ts @@ -0,0 +1,8 @@ +import type { PlateEditor } from '@udecode/plate-core'; +import type { Value } from '@udecode/slate'; + +export const addSelectedRow = ( + editor: PlateEditor, + id: string, + options: { aboveHtmlNode?: HTMLDivElement; clear?: boolean } = {} +) => (editor as any).addSelectedRow(id, options); diff --git a/packages/plate-utils/src/shared/index.ts b/packages/plate-utils/src/shared/index.ts index eaf0d00ead..db2f2bdf6d 100644 --- a/packages/plate-utils/src/shared/index.ts +++ b/packages/plate-utils/src/shared/index.ts @@ -12,3 +12,4 @@ export * from './moveSelectionByOffset'; export * from './onKeyDownToggleElement'; export * from './onKeyDownToggleMark'; export * from './types/index'; +export * from './addSelectedRow' diff --git a/packages/selection/src/blockSelectionStore.ts b/packages/selection/src/blockSelectionStore.ts index 3943cdcd2b..5a0c65bf53 100644 --- a/packages/selection/src/blockSelectionStore.ts +++ b/packages/selection/src/blockSelectionStore.ts @@ -2,6 +2,7 @@ import { createZustandStore } from '@udecode/plate-common/server'; import type { ChangedElements } from './components/SelectionArea'; +import { getAllSelectableDomNode, getSelectedDomNode } from './utils'; import { extractSelectableIds } from './utils/extractSelectableIds'; export const blockSelectionStore = createZustandStore('selection')({ @@ -27,6 +28,38 @@ export const blockSelectionStore = createZustandStore('selection')({ set.isSelecting(false); }, })) + .extendActions((set) => ({ + addSelectedRow: ( + id: string, + options: { aboveHtmlNode?: HTMLDivElement; clear?: boolean } = {} + ) => { + const { aboveHtmlNode, clear = true } = options; + + const element = aboveHtmlNode ?? getSelectedDomNode(id); + + if (!element) return; + + const selectedIds = blockSelectionSelectors.selectedIds(); + + if (!selectedIds.has(id) && clear) { + set.resetSelectedIds(); + } + + set.setSelectedIds({ + added: [element], + removed: [], + }); + }, + selectedAll: () => { + const all = getAllSelectableDomNode(); + set.resetSelectedIds(); + + set.setSelectedIds({ + added: Array.from(all), + removed: [], + }); + }, + })) .extendSelectors((set, get) => ({ isSelected: (id?: string) => id && get.selectedIds().has(id), isSelectingSome: () => get.selectedIds().size > 0, diff --git a/packages/selection/src/components/BlockSelectable.tsx b/packages/selection/src/components/BlockSelectable.tsx index 6254fe9f0f..cbbed72496 100644 --- a/packages/selection/src/components/BlockSelectable.tsx +++ b/packages/selection/src/components/BlockSelectable.tsx @@ -1,18 +1,25 @@ -import React from 'react'; +import React, { useRef } from 'react'; import { findNodePath, useEditorRef } from '@udecode/plate-common'; import { type TElement, + getAboveNode, getPluginOptions, isInline, + isVoid, queryNode, } from '@udecode/plate-common/server'; +import { Path } from 'slate'; -import { useBlockSelectionSelectors } from '../blockSelectionStore'; +import { + blockSelectionActions, + useBlockSelectionSelectors, +} from '../blockSelectionStore'; import { type BlockSelectionPlugin, KEY_BLOCK_SELECTION, } from '../createBlockSelectionPlugin'; +import { isBlockSelected } from '../queries'; export interface BlockSelectableOptions { element: TElement; @@ -26,6 +33,7 @@ export const useBlockSelectableState = ({ selectedColor, }: BlockSelectableOptions) => { const editor = useEditorRef(); + const ref = useRef(null); const path = React.useMemo( () => findNodePath(editor, element), @@ -51,14 +59,19 @@ export const useBlockSelectableState = ({ return { active: active ?? true, + editor, element, + path, + ref, selectedColor, }; }; export const useBlockSelectable = ({ + editor, element, - selectedColor, + path, + ref, }: ReturnType) => { const id = element?.id as string | undefined; const isSelected = useBlockSelectionSelectors().isSelected(id); @@ -73,11 +86,40 @@ export const useBlockSelectable = ({ ? 'slate-selected slate-selectable' : 'slate-selectable', key: id, - style: isSelected - ? { - backgroundColor: selectedColor, + onContextMenu: (event: React.MouseEvent) => { + if (!editor) return; + if (editor.selection?.focus) { + const nodeEntry = getAboveNode(editor); + + if (nodeEntry && Path.isCommon(path, nodeEntry[1])) { + const isSelected = isBlockSelected(nodeEntry[0] as TElement); + const isOpenAlways = + (event.target as HTMLElement).dataset?.openContextMenu === 'true'; + + /** + * When "block selected or is void or has openContextMenu props", + * right click can always open the context menu. + */ + if (!isSelected && !isVoid(editor, nodeEntry[0]) && !isOpenAlways) + return event.stopPropagation(); } - : undefined, + } + + const aboveHtmlNode = ref.current; + + if (id && aboveHtmlNode) { + blockSelectionActions.addSelectedRow(id, { + aboveHtmlNode, + clear: !event?.shiftKey, + }); + } + }, + ref, + // style: isSelected + // ? { + // backgroundColor: selectedColor, + // } + // : undefined, ...data, }, }; diff --git a/packages/selection/src/components/BlockSelection.tsx b/packages/selection/src/components/BlockSelection.tsx new file mode 100644 index 0000000000..4b7f4d87b6 --- /dev/null +++ b/packages/selection/src/components/BlockSelection.tsx @@ -0,0 +1,36 @@ +import React from 'react'; + +import { BlockSelectionArea } from './BlockSelectionArea'; +import { BlockStartArea } from './BlockStartArea'; + +export const BlockSelection = ({ children }: any) => { + return ( + + + + + + {children} + + ); +}; diff --git a/packages/selection/src/components/BlockStartArea.tsx b/packages/selection/src/components/BlockStartArea.tsx index 102e18d8af..60049a8517 100644 --- a/packages/selection/src/components/BlockStartArea.tsx +++ b/packages/selection/src/components/BlockStartArea.tsx @@ -12,6 +12,10 @@ export const useBlockStartArea = ({ return { props: { className: `slate-start-area slate-start-area-${placement}`, + onContextMenu: (e: React.MouseEvent) => { + e.stopPropagation(); + e.preventDefault(); + }, style: { bottom: ['bottom'].includes(placement) ? 0 : undefined, cursor: 'text', diff --git a/packages/selection/src/components/index.ts b/packages/selection/src/components/index.ts index 2d2dfb7080..1ea1ae67f4 100644 --- a/packages/selection/src/components/index.ts +++ b/packages/selection/src/components/index.ts @@ -3,6 +3,7 @@ */ export * from './BlockSelectable'; +export * from './BlockSelection'; export * from './BlockSelectionArea'; export * from './BlockStartArea'; export * from './SelectionArea'; diff --git a/packages/selection/src/context-menu/blockContextMenuStore.ts b/packages/selection/src/context-menu/blockContextMenuStore.ts new file mode 100644 index 0000000000..eb60797a98 --- /dev/null +++ b/packages/selection/src/context-menu/blockContextMenuStore.ts @@ -0,0 +1,46 @@ +import { createZustandStore } from '@udecode/plate-common/server'; + +/** + * The show action has depend on blockSelection store a lot. should I move this + * action into blockSelection store? + */ +export const blockContextMenuStore = createZustandStore('contextMenu')({ + action: { group: null, value: null } as { + group: null | string; + value: null | string; + } | null, + anchorRect: { x: 0, y: 0 }, + openEditorId: null as null | string, + store: null as any | null, +}) + .extendActions((set) => ({ + reset: () => { + set.anchorRect({ x: 0, y: 0 }); + }, + })) + .extendActions((set, get) => ({ + hide: () => { + set.openEditorId(null); + set.reset(); + }, + show: ( + editorId: string, + event: React.MouseEvent + ) => { + set.openEditorId(editorId); + get.store().show(); + get.store().setAutoFocusOnShow(true); + get.store().setInitialFocus('first'); + + set.anchorRect({ x: event.clientX, y: event.clientY }); + }, + })) + .extendSelectors((state) => ({ + isOpen: (editorId: string) => state.openEditorId === editorId, + })); + +export const blockContextMenuActions = blockContextMenuStore.set; + +export const blockContextMenuSelectors = blockContextMenuStore.get; + +export const useBlockContextMenuSelectors = () => blockContextMenuStore.use; diff --git a/packages/selection/src/context-menu/index.ts b/packages/selection/src/context-menu/index.ts new file mode 100644 index 0000000000..f0ff5bb6a8 --- /dev/null +++ b/packages/selection/src/context-menu/index.ts @@ -0,0 +1,8 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './blockContextMenuStore'; +export * from './types'; +export * from './useBlockContextMenu'; +export * from './useBlockMenuItems'; diff --git a/packages/selection/src/context-menu/types.ts b/packages/selection/src/context-menu/types.ts new file mode 100644 index 0000000000..a6932dbca4 --- /dev/null +++ b/packages/selection/src/context-menu/types.ts @@ -0,0 +1,14 @@ +export type CommandItem = { + shortcut?: string; + title: string; + value: string; +}; + +export interface Menu { + heading: string; + items: CommandItem[]; +} + +export const ACTION_DELETE = 'context_menu_delete'; + +export const ACTION_COPY = 'context_menu_copy'; diff --git a/packages/selection/src/context-menu/useBlockContextMenu.ts b/packages/selection/src/context-menu/useBlockContextMenu.ts new file mode 100644 index 0000000000..1e678987f6 --- /dev/null +++ b/packages/selection/src/context-menu/useBlockContextMenu.ts @@ -0,0 +1,67 @@ +import { useCallback, useMemo } from 'react'; + +import { getNodeTexts, setNodes, useEditorRef } from '@udecode/plate-common'; + +import { useBlockSelectionSelectors } from '../blockSelectionStore'; +import { getSelectedBlocks } from '../queries'; +import { + blockContextMenuActions, + blockContextMenuSelectors, + useBlockContextMenuSelectors, +} from './blockContextMenuStore'; + +export const useBlockContextMenuState = () => { + const editor = useEditorRef(); + + const isOpen = useBlockContextMenuSelectors().isOpen(editor.id); + const selectedIds = useBlockSelectionSelectors().selectedIds(); + + // eslint-disable-next-line react-hooks/exhaustive-deps + const action = useMemo(() => blockContextMenuSelectors.action(), [isOpen]); + const setAction = blockContextMenuActions.action; + const selectedBlocks = getSelectedBlocks(editor); + + const setMarkSelection = useCallback( + (mark: string, value: string) => { + selectedBlocks.forEach(([node, nodePath]) => { + const _textEntry = getNodeTexts(node); + const textEntry = Array.from(_textEntry); + textEntry.forEach(([_, textpath]) => { + setNodes( + editor, + { [mark]: value }, + { at: [...nodePath, ...textpath] } + ); + }); + }); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [action] + ); + + return { + action, + editor, + isOpen, + selectedBlocks, + selectedIds, + setAction, + setMarkSelection, + }; +}; + +export const useBlockContextMenu = ({ + editor, +}: ReturnType) => { + return { + props: { + // onOpenChange: (value: boolean) => { + // if (value) { + // blockContextMenuActions.show(editor.id); + // } else { + // blockContextMenuActions.hide(); + // } + // }, + }, + }; +}; diff --git a/packages/selection/src/context-menu/useBlockMenuItems.ts b/packages/selection/src/context-menu/useBlockMenuItems.ts new file mode 100644 index 0000000000..d5f4fe4277 --- /dev/null +++ b/packages/selection/src/context-menu/useBlockMenuItems.ts @@ -0,0 +1,24 @@ +import { useEditorRef } from '@udecode/plate-common'; + +import { useBlockContextMenuSelectors } from '.'; +import { useBlockSelectionSelectors } from '../blockSelectionStore'; +import { getSelectedBlocks } from '../queries'; + +export const useBlockMenuItemsState = () => { + const editor = useEditorRef(); + + const isOpen = useBlockContextMenuSelectors().isOpen(editor.id); + const selectedIds = useBlockSelectionSelectors().selectedIds(); + + const selectedBlocks = getSelectedBlocks(editor); + + return { + isOpen, + selectedBlocks, + selectedIds, + }; +}; + +export const useBlockMenuItems = () => { + return {}; +}; diff --git a/packages/selection/src/createBlockSelectionPlugin.tsx b/packages/selection/src/createBlockSelectionPlugin.tsx index 37ba6b4fc4..eb0bf84570 100644 --- a/packages/selection/src/createBlockSelectionPlugin.tsx +++ b/packages/selection/src/createBlockSelectionPlugin.tsx @@ -5,11 +5,12 @@ import { createPluginFactory, } from '@udecode/plate-common/server'; +import { BlockSelectionArea, BlockStartArea } from './components'; import { BlockSelectable } from './components/BlockSelectable'; -import { BlockSelectionArea } from './components/BlockSelectionArea'; -import { BlockStartArea } from './components/BlockStartArea'; -import { onChangeBlockSelection } from './onChangeBlockSelection'; +import { onKeyDownSelection } from './onKeyDownSelection'; import { useHooksBlockSelection } from './useHooksBlockSelection'; +import { onCloseBlockSelection } from './utils'; +import { withSelection } from './withSelection'; export const KEY_BLOCK_SELECTION = 'blockSelection'; @@ -27,7 +28,12 @@ export interface BlockSelectionPlugin { export const createBlockSelectionPlugin = createPluginFactory({ handlers: { - onChange: onChangeBlockSelection, + onChange: onCloseBlockSelection, + // onFocus: onCloseBlockSelection, + onKeyDown: onKeyDownSelection, + onMouseDown: () => (e) => { + if (e.button === 2) e.preventDefault(); + }, }, inject: { aboveComponent: @@ -85,4 +91,5 @@ export const createBlockSelectionPlugin = ), }), useHooks: useHooksBlockSelection, + withOverrides: withSelection, }); diff --git a/packages/selection/src/hooks/index.ts b/packages/selection/src/hooks/index.ts new file mode 100644 index 0000000000..ba66a2aec4 --- /dev/null +++ b/packages/selection/src/hooks/index.ts @@ -0,0 +1,5 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './useBlockSelected'; diff --git a/packages/selection/src/hooks/useBlockSelected.ts b/packages/selection/src/hooks/useBlockSelected.ts new file mode 100644 index 0000000000..61a61a3569 --- /dev/null +++ b/packages/selection/src/hooks/useBlockSelected.ts @@ -0,0 +1,9 @@ +import { useBlockSelectionSelectors } from '../blockSelectionStore'; + +export const useBlockSelected = (id?: string) => { + return useBlockSelectionSelectors().isSelected(id); +}; + +export const useHasBlockSelected = () => { + return useBlockSelectionSelectors().selectedIds().size > 0; +}; diff --git a/packages/selection/src/index.ts b/packages/selection/src/index.ts index 0260360f1a..5bf75378fc 100644 --- a/packages/selection/src/index.ts +++ b/packages/selection/src/index.ts @@ -4,8 +4,11 @@ export * from './blockSelectionStore'; export * from './createBlockSelectionPlugin'; -export * from './onChangeBlockSelection'; +export * from './onKeyDownSelection'; export * from './useHooksBlockSelection'; +export * from './withSelection'; export * from './components/index'; +export * from './context-menu/index'; +export * from './hooks/index'; export * from './queries/index'; export * from './utils/index'; diff --git a/packages/selection/src/onChangeBlockSelection.ts b/packages/selection/src/onChangeBlockSelection.ts deleted file mode 100644 index 42ccb2c507..0000000000 --- a/packages/selection/src/onChangeBlockSelection.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { PlateEditor, Value } from '@udecode/plate-common/server'; - -import { - blockSelectionActions, - blockSelectionSelectors, -} from './blockSelectionStore'; - -export const onChangeBlockSelection = - = PlateEditor>( - editor: E - ) => - () => { - if (editor.selection && blockSelectionSelectors.isSelecting()) { - blockSelectionActions.unselect(); - } - }; diff --git a/packages/selection/src/onKeyDownSelection.ts b/packages/selection/src/onKeyDownSelection.ts new file mode 100644 index 0000000000..55b2becb15 --- /dev/null +++ b/packages/selection/src/onKeyDownSelection.ts @@ -0,0 +1,48 @@ +import { + type KeyboardHandlerReturnType, + type PlateEditor, + type Value, + getAncestorNode, + isHotkey, + isRangeInSameBlock, + isSelectionCoverBlock, + select, +} from '@udecode/plate-common/server'; + +import { blockSelectionActions } from './blockSelectionStore'; + +export const onKeyDownSelection = + = PlateEditor>( + editor: E + ): KeyboardHandlerReturnType => + (event) => { + if (isHotkey('mod+a', event)) { + const ancestorNode = getAncestorNode(editor); + + if (!ancestorNode) return; + + const [, path] = ancestorNode; + + if (isSelectionCoverBlock(editor)) { + return blockSelectionActions.selectedAll(); + } + // TODO: should select the blocks then selected all should exclude table and columns + if (!isRangeInSameBlock(editor)) { + return blockSelectionActions.selectedAll(); + } + + select(editor, path); + + event.preventDefault(); + event.stopPropagation(); + } + if (isHotkey('escape', event)) { + const ancestorNode = getAncestorNode(editor); + const id = ancestorNode?.[0].id; + + blockSelectionActions.addSelectedRow(id); + + event.preventDefault(); + event.stopPropagation(); + } + }; diff --git a/packages/selection/src/queries/getSelectedBlocks.ts b/packages/selection/src/queries/getSelectedBlocks.ts index 32be77d682..41985ac739 100644 --- a/packages/selection/src/queries/getSelectedBlocks.ts +++ b/packages/selection/src/queries/getSelectedBlocks.ts @@ -17,3 +17,9 @@ export const getSelectedBlocks = (editor: TEditor) => { }), ]; }; + +export const isBlockSelected = (node: TElement) => { + const selectedIds = blockSelectionSelectors.selectedIds(); + + return node.id && selectedIds.has(node.id); +}; diff --git a/packages/selection/src/useHooksBlockSelection.ts b/packages/selection/src/useHooksBlockSelection.ts index 6ac9f12349..aa69b42e9e 100644 --- a/packages/selection/src/useHooksBlockSelection.ts +++ b/packages/selection/src/useHooksBlockSelection.ts @@ -7,6 +7,8 @@ import { type WithPlatePlugin, findNode, getEndPoint, + getNextNode, + getPreviousNode, isHotkey, removeNodes, } from '@udecode/plate-common/server'; @@ -18,6 +20,7 @@ import { blockSelectionSelectors, useBlockSelectionSelectors, } from './blockSelectionStore'; +import { useBlockContextMenuSelectors } from './context-menu'; import { copySelectedBlocks } from './utils/copySelectedBlocks'; import { selectInsertedBlocks } from './utils/index'; import { pasteSelectedBlocks } from './utils/pasteSelectedBlocks'; @@ -32,6 +35,7 @@ export const useHooksBlockSelection = < const { onKeyDownSelecting } = options; const isSelecting = useBlockSelectionSelectors().isSelecting(); const selectedIds = useBlockSelectionSelectors().selectedIds(); + const isOpen = useBlockContextMenuSelectors().isOpen(editor.id); // TODO: test React.useEffect(() => { @@ -48,7 +52,7 @@ export const useHooksBlockSelection = < input.setAttribute('id', 'slate-shadow-input'); // no scrolling on focus input.style.position = 'fixed'; - input.style.zIndex = '10000'; + input.style.zIndex = '999'; // hide input.style.top = '-300px'; input.style.left = '-300px'; @@ -75,9 +79,12 @@ export const useHooksBlockSelection = < if (isHotkey('enter')(e)) { // get the first block in the selection const entry = findNode(editor, { + at: [], match: (n) => blockSelectionSelectors.selectedIds().has(n.id), }); + console.log(entry); + if (entry) { const [, path] = entry; @@ -92,6 +99,32 @@ export const useHooksBlockSelection = < match: (n) => blockSelectionSelectors.selectedIds().has(n.id), }); } + // TODO: skip toggle child + if (isHotkey('up')(e)) { + const firstId = [...blockSelectionSelectors.selectedIds()][0]; + const node = findNode(editor, { + at: [], + match: (n) => n.id === firstId, + }); + const prev = getPreviousNode(editor, { + at: node?.[1], + }); + + const prevId = prev?.[0].id; + blockSelectionActions.addSelectedRow(prevId); + } + if (isHotkey('down')(e)) { + const lastId = [...blockSelectionSelectors.selectedIds()].pop(); + const node = findNode(editor, { + at: [], + match: (n) => n.id === lastId, + }); + const next = getNextNode(editor, { + at: node?.[1], + }); + const nextId = next?.[0].id; + blockSelectionActions.addSelectedRow(nextId); + } }); // TODO: paste + select blocks if selecting editor @@ -113,6 +146,8 @@ export const useHooksBlockSelection = < at: [], match: (n) => blockSelectionSelectors.selectedIds().has(n.id), }); + + focusEditor(editor); } } }); @@ -126,5 +161,5 @@ export const useHooksBlockSelection = < document.body.append(input); input.focus(); } - }, [editor, isSelecting, onKeyDownSelecting, selectedIds]); + }, [editor, isSelecting, onKeyDownSelecting, selectedIds, isOpen]); }; diff --git a/packages/selection/src/utils/extractSelectableIds.ts b/packages/selection/src/utils/extractSelectableIds.ts index e51826ef79..fd8b6c5b82 100644 --- a/packages/selection/src/utils/extractSelectableIds.ts +++ b/packages/selection/src/utils/extractSelectableIds.ts @@ -1,3 +1,6 @@ export const extractSelectableIds = (els: Element[]): any[] => { return els.map((v) => (v as HTMLElement).dataset.key); }; + +export const extractSelectableId = (el: Element) => + (el as HTMLElement).dataset.key; diff --git a/packages/selection/src/utils/getAboveDomNode.ts b/packages/selection/src/utils/getAboveDomNode.ts new file mode 100644 index 0000000000..668bda9838 --- /dev/null +++ b/packages/selection/src/utils/getAboveDomNode.ts @@ -0,0 +1,7 @@ +export const getSelectedDomNode = (id: string) => { + return document.querySelector(`.slate-selectable[data-key="${id}"]`); +}; + +export const getAllSelectableDomNode = () => { + return document.querySelectorAll(`.slate-selectable`); +}; diff --git a/packages/selection/src/utils/index.ts b/packages/selection/src/utils/index.ts index f5021402cb..39c67cd17b 100644 --- a/packages/selection/src/utils/index.ts +++ b/packages/selection/src/utils/index.ts @@ -4,5 +4,7 @@ export * from './copySelectedBlocks'; export * from './extractSelectableIds'; +export * from './getAboveDomNode'; +export * from './onCloseBlockSelection'; export * from './pasteSelectedBlocks'; export * from './selectInsertedBlocks'; diff --git a/packages/selection/src/utils/onCloseBlockSelection.ts b/packages/selection/src/utils/onCloseBlockSelection.ts new file mode 100644 index 0000000000..5eea48813e --- /dev/null +++ b/packages/selection/src/utils/onCloseBlockSelection.ts @@ -0,0 +1,25 @@ +import type { PlateEditor, Value } from '@udecode/plate-common/server'; + +import { + blockSelectionActions, + blockSelectionSelectors, +} from '../blockSelectionStore'; +import { + blockContextMenuActions, + blockContextMenuSelectors, +} from '../context-menu'; + +export const onCloseBlockSelection = + = PlateEditor>( + editor: E + ) => + () => { + if ( + editor.selection && + blockSelectionSelectors.isSelecting() && + !blockContextMenuSelectors.isOpen(editor.id) + ) { + blockSelectionActions.unselect(); + blockContextMenuActions.hide(); + } + }; diff --git a/packages/selection/src/withSelection.ts b/packages/selection/src/withSelection.ts new file mode 100644 index 0000000000..a1742f4986 --- /dev/null +++ b/packages/selection/src/withSelection.ts @@ -0,0 +1,14 @@ +import type { PlateEditor, Value } from '@udecode/plate-common/server'; + +import { blockSelectionActions } from './blockSelectionStore'; + +export const withSelection = < + V extends Value = Value, + E extends PlateEditor = PlateEditor, +>( + editor: E +) => { + (editor as any).addSelectedRow = blockSelectionActions.addSelectedRow; + + return editor; +}; diff --git a/packages/slate-utils/src/queries/getAncestorNode.ts b/packages/slate-utils/src/queries/getAncestorNode.ts new file mode 100644 index 0000000000..43e17be328 --- /dev/null +++ b/packages/slate-utils/src/queries/getAncestorNode.ts @@ -0,0 +1,17 @@ +import type { Path } from 'slate'; + +import { type TEditor, type Value, getNodeEntry } from '@udecode/slate'; + +/** Get the top-level block. */ +export const getAncestorNode = ( + editor: TEditor, + path?: Path +) => { + const { selection } = editor; + + const at = path ? path[0] : selection?.focus?.path[0]; + + if (!at) return; + + return getNodeEntry(editor, [at]); +}; diff --git a/packages/slate-utils/src/queries/getNodesRange.ts b/packages/slate-utils/src/queries/getNodesRange.ts new file mode 100644 index 0000000000..61492ff0c4 --- /dev/null +++ b/packages/slate-utils/src/queries/getNodesRange.ts @@ -0,0 +1,19 @@ +import { + type TEditor, + type TNodeEntry, + type Value, + getRange, +} from '@udecode/slate'; + +/** Get node entries range. */ +export const getNodesRange = ( + editor: TEditor, + nodeEntries: TNodeEntry[] +) => { + if (nodeEntries.length === 0) return; + + const firstBlockPath = nodeEntries[0][1]; + const lastBlockPath = nodeEntries.at(-1)![1]; + + return getRange(editor, firstBlockPath, lastBlockPath); +}; diff --git a/packages/slate-utils/src/queries/index.ts b/packages/slate-utils/src/queries/index.ts index 3fb353796d..ad25bffcab 100644 --- a/packages/slate-utils/src/queries/index.ts +++ b/packages/slate-utils/src/queries/index.ts @@ -3,6 +3,7 @@ */ export * from './findDescendant'; +export * from './getAncestorNode'; export * from './getBlockAbove'; export * from './getChildren'; export * from './getEdgeBlocksAbove'; @@ -11,6 +12,7 @@ export * from './getLastNodeByLevel'; export * from './getMark'; export * from './getNextNodeStartPoint'; export * from './getNextSiblingNodes'; +export * from './getNodesRange'; export * from './getOperations'; export * from './getPointBeforeLocation'; export * from './getPointFromLocation'; @@ -35,6 +37,7 @@ export * from './isRangeInSameBlock'; export * from './isRangeInSingleText'; export * from './isSelectionAtBlockEnd'; export * from './isSelectionAtBlockStart'; +export * from './isSelectionCoverBlock'; export * from './isSelectionExpanded'; export * from './isTextByPath'; export * from './isWordAfterTrigger'; diff --git a/packages/slate-utils/src/queries/isSelectionCoverBlock.ts b/packages/slate-utils/src/queries/isSelectionCoverBlock.ts new file mode 100644 index 0000000000..dcd7c4a7cc --- /dev/null +++ b/packages/slate-utils/src/queries/isSelectionCoverBlock.ts @@ -0,0 +1,21 @@ +import type { GetAboveNodeOptions, TEditor, Value } from '@udecode/slate'; +import type { Range } from 'slate'; + +import { isRangeInSameBlock } from './isRangeInSameBlock'; +import { isSelectionAtBlockEnd } from './isSelectionAtBlockEnd'; +import { isSelectionAtBlockStart } from './isSelectionAtBlockStart'; + +// TODO: test +export const isSelectionCoverBlock = ( + editor: TEditor, + { + at, + ...options + }: { at?: Range | null } & Omit, 'at'> = {} +) => { + return ( + isSelectionAtBlockEnd(editor, options) && + isSelectionAtBlockStart(editor, options) && + isRangeInSameBlock(editor, options) + ); +}; diff --git a/yarn.lock b/yarn.lock index 8bd0b6eef4..0735044962 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4952,6 +4952,13 @@ __metadata: languageName: node linkType: hard +"@types/katex@npm:0.16.7": + version: 0.16.7 + resolution: "@types/katex@npm:0.16.7" + checksum: 10c0/68dcb9f68a90513ec78ca0196a142e15c2a2c270b1520d752bafd47a99207115085a64087b50140359017d7e9c870b3c68e7e4d36668c9e348a9ef0c48919b5a + languageName: node + linkType: hard + "@types/liftoff@npm:^4.0.0": version: 4.0.3 resolution: "@types/liftoff@npm:4.0.3" @@ -5692,6 +5699,22 @@ __metadata: languageName: unknown linkType: soft +"@udecode/plate-callout@workspace:packages/callout": + version: 0.0.0-use.local + resolution: "@udecode/plate-callout@workspace:packages/callout" + dependencies: + "@udecode/plate-common": "workspace:^" + peerDependencies: + "@udecode/plate-common": ">=31.3.2" + react: ">=16.8.0" + react-dom: ">=16.8.0" + slate: ">=0.94.0" + slate-history: ">=0.93.0" + slate-hyperscript: ">=0.66.0" + slate-react: ">=0.99.0" + languageName: unknown + linkType: soft + "@udecode/plate-caption@workspace:^, @udecode/plate-caption@workspace:packages/caption": version: 0.0.0-use.local resolution: "@udecode/plate-caption@workspace:packages/caption" @@ -6153,6 +6176,24 @@ __metadata: languageName: unknown linkType: soft +"@udecode/plate-math@workspace:packages/math": + version: 0.0.0-use.local + resolution: "@udecode/plate-math@workspace:packages/math" + dependencies: + "@types/katex": "npm:0.16.7" + "@udecode/plate-common": "workspace:^" + katex: "npm:0.16.10" + peerDependencies: + "@udecode/plate-common": ">=33.0.4" + react: ">=16.8.0" + react-dom: ">=16.8.0" + slate: ">=0.94.0" + slate-history: ">=0.93.0" + slate-hyperscript: ">=0.66.0" + slate-react: ">=0.99.0" + languageName: unknown + linkType: soft + "@udecode/plate-media@npm:33.0.2, @udecode/plate-media@workspace:^, @udecode/plate-media@workspace:packages/media": version: 0.0.0-use.local resolution: "@udecode/plate-media@workspace:packages/media" @@ -8188,6 +8229,13 @@ __metadata: languageName: node linkType: hard +"commander@npm:^8.3.0": + version: 8.3.0 + resolution: "commander@npm:8.3.0" + checksum: 10c0/8b043bb8322ea1c39664a1598a95e0495bfe4ca2fad0d84a92d7d1d8d213e2a155b441d2470c8e08de7c4a28cf2bc6e169211c49e1b21d9f7edc6ae4d9356060 + languageName: node + linkType: hard + "comment-json@npm:^4.2.3": version: 4.2.3 resolution: "comment-json@npm:4.2.3" @@ -13469,6 +13517,17 @@ __metadata: languageName: node linkType: hard +"katex@npm:0.16.10": + version: 0.16.10 + resolution: "katex@npm:0.16.10" + dependencies: + commander: "npm:^8.3.0" + bin: + katex: cli.js + checksum: 10c0/b465213157e5245bbb31ff6563c33ae81807c06d6f2246325b3a2397497e8929a34eebbb262f5e0991ec00fbc0cc85f388246e6dfc38ec86c28d3e481cb70afa + languageName: node + linkType: hard + "keyv@npm:^4.5.3": version: 4.5.4 resolution: "keyv@npm:4.5.4" From 6cece9b358a5b23d7a0e6d8f8638f42e79378355 Mon Sep 17 00:00:00 2001 From: FelixFeng Date: Thu, 6 Jun 2024 14:23:51 +0800 Subject: [PATCH 44/63] fix: ci changelog.mdx --- .../www/content/docs/components/changelog.mdx | 6 ++++ .../registry/default/plate-ui/draggable.tsx | 35 ++++++++++++------- .../default/plate-ui/floating-toolbar.tsx | 12 ++++++- packages/callout/tsconfig.build.json | 8 +++++ 4 files changed, 48 insertions(+), 13 deletions(-) create mode 100644 packages/callout/tsconfig.build.json diff --git a/apps/www/content/docs/components/changelog.mdx b/apps/www/content/docs/components/changelog.mdx index 07c1d6f2b7..dceda65646 100644 --- a/apps/www/content/docs/components/changelog.mdx +++ b/apps/www/content/docs/components/changelog.mdx @@ -8,6 +8,12 @@ Since Plate UI is not a component library, a changelog is maintained here. Use the [CLI](https://platejs.org/docs/components/cli) to install the latest version of the components. +## June 2024 #11 + +### June 6 #11.1 +- feat: `draggable` Add the data-key attribute to facilitate adding selection after the drag-and-drop operation is completed + + ## May 2024 #10 ### May 30 #10.2 diff --git a/apps/www/src/registry/default/plate-ui/draggable.tsx b/apps/www/src/registry/default/plate-ui/draggable.tsx index 1c5cdeac21..88619cf789 100644 --- a/apps/www/src/registry/default/plate-ui/draggable.tsx +++ b/apps/www/src/registry/default/plate-ui/draggable.tsx @@ -2,23 +2,32 @@ import React from 'react'; -import type { - ClassNames, - PlateElementProps, - TEditor, - TElement, -} from '@udecode/plate-common'; -import type { DragItemNode } from '@udecode/plate-dnd'; import type { DropTargetMonitor } from 'react-dnd'; import { cn, withRef } from '@udecode/cn'; -import { useEditorRef, useElement } from '@udecode/plate-common'; -import { useDraggable, useDraggableState } from '@udecode/plate-dnd'; +import { + type ClassNames, + type PlateElementProps, + type TEditor, + type TElement, + useEditorRef, + useElement, +} from '@udecode/plate-common'; +import { + type DragItemNode, + useDraggable, + useDraggableState, +} from '@udecode/plate-dnd'; import { blockSelectionActions } from '@udecode/plate-selection'; import { Icons } from '@/components/icons'; -import { Tooltip, TooltipContent, TooltipTrigger } from './tooltip'; +import { + Tooltip, + TooltipContent, + TooltipPortal, + TooltipTrigger, +} from './tooltip'; export interface DraggableProps extends PlateElementProps, @@ -74,7 +83,7 @@ const DragHandle = () => { return ( - + { @@ -91,7 +100,9 @@ const DragHandle = () => { }} /> - Drag to move + + Drag to move + ); }; diff --git a/apps/www/src/registry/default/plate-ui/floating-toolbar.tsx b/apps/www/src/registry/default/plate-ui/floating-toolbar.tsx index e61a0a1aac..09c412e6fe 100644 --- a/apps/www/src/registry/default/plate-ui/floating-toolbar.tsx +++ b/apps/www/src/registry/default/plate-ui/floating-toolbar.tsx @@ -3,7 +3,12 @@ import React from 'react'; import { cn, withRef } from '@udecode/cn'; -import { PortalBody, useComposedRef } from '@udecode/plate-common'; +import { + PortalBody, + useComposedRef, + useEventEditorSelectors, + usePlateSelectors, +} from '@udecode/plate-common'; import { type FloatingToolbarState, flip, @@ -20,7 +25,12 @@ export const FloatingToolbar = withRef< state?: FloatingToolbarState; } >(({ children, state, ...props }, componentRef) => { + const editorId = usePlateSelectors().id(); + const focusedEditorId = useEventEditorSelectors.focus(); + const floatingToolbarState = useFloatingToolbarState({ + editorId, + focusedEditorId, ...state, floatingOptions: { middleware: [ diff --git a/packages/callout/tsconfig.build.json b/packages/callout/tsconfig.build.json new file mode 100644 index 0000000000..425481e027 --- /dev/null +++ b/packages/callout/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "../../config/tsconfig.build.json", + "compilerOptions": { + "declarationDir": "./dist", + "outDir": "./dist" + }, + "include": ["src"] +} From ae516f636b994a8e9724d1bea5395a13c93bc021 Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Thu, 6 Jun 2024 10:12:59 +0100 Subject: [PATCH 45/63] Update components changelog for the recent combobox rework --- apps/www/content/docs/components/changelog.mdx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/apps/www/content/docs/components/changelog.mdx b/apps/www/content/docs/components/changelog.mdx index 07c1d6f2b7..d4abe8e293 100644 --- a/apps/www/content/docs/components/changelog.mdx +++ b/apps/www/content/docs/components/changelog.mdx @@ -8,6 +8,15 @@ Since Plate UI is not a component library, a changelog is maintained here. Use the [CLI](https://platejs.org/docs/components/cli) to install the latest version of the components. +## June 2024 #11 + +### June 06 #11.1 + +- replace `combobox` with `inline-combobox` +- remove `mention-combobox`, `emoji-combobox` and `slash-combobox` +- add `emoji-input-element` +- update `mention-input-element` and `slash-input-element` to use the new combobox + ## May 2024 #10 ### May 30 #10.2 From e5678b1c00e3b121de8b6e51f8acd80b9f12f95d Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Thu, 6 Jun 2024 10:14:46 +0100 Subject: [PATCH 46/63] Fix: US-style dates don't have leading zeros --- apps/www/content/docs/components/changelog.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/www/content/docs/components/changelog.mdx b/apps/www/content/docs/components/changelog.mdx index d4abe8e293..8a8410a56a 100644 --- a/apps/www/content/docs/components/changelog.mdx +++ b/apps/www/content/docs/components/changelog.mdx @@ -10,7 +10,7 @@ Use the [CLI](https://platejs.org/docs/components/cli) to install the latest ver ## June 2024 #11 -### June 06 #11.1 +### June 6 #11.1 - replace `combobox` with `inline-combobox` - remove `mention-combobox`, `emoji-combobox` and `slash-combobox` From d969b3d82bcc5acd8ca482c6ca9e83efa28d4431 Mon Sep 17 00:00:00 2001 From: zbeyens Date: Thu, 6 Jun 2024 12:43:24 +0200 Subject: [PATCH 47/63] doc --- .changeset/breezy-beers-end.md | 4 ++-- .changeset/four-goats-stare.md | 4 ++-- .changeset/happy-masks-jump.md | 4 ++-- .changeset/lazy-poets-buy.md | 4 ++-- .changeset/long-lobsters-judge.md | 4 ++-- .changeset/many-pillows-unite.md | 4 ++-- .changeset/ninety-knives-wash.md | 4 ++-- .changeset/pink-yaks-smoke.md | 4 ++-- .changeset/smart-flies-relate.md | 4 ++-- .changeset/thin-cycles-itch.md | 4 ++-- .changeset/wicked-cheetahs-laugh.md | 4 ++-- .changeset/wise-pandas-thank.md | 4 ++-- .changeset/yellow-gifts-vanish.md | 4 ++-- .changeset/young-pigs-behave.md | 4 ++-- .changeset/young-sheep-rush.md | 4 ++-- 15 files changed, 30 insertions(+), 30 deletions(-) diff --git a/.changeset/breezy-beers-end.md b/.changeset/breezy-beers-end.md index 66e31f6468..c3aa8961b5 100644 --- a/.changeset/breezy-beers-end.md +++ b/.changeset/breezy-beers-end.md @@ -1,5 +1,5 @@ --- -"@udecode/plate-floating": patch +'@udecode/plate-floating': patch --- -Only pop up `floating-toolbar` after the selection is complete. +Fix: only pop up `floating-toolbar` after the selection is complete. diff --git a/.changeset/four-goats-stare.md b/.changeset/four-goats-stare.md index 6d7583ff98..ec78b545d0 100644 --- a/.changeset/four-goats-stare.md +++ b/.changeset/four-goats-stare.md @@ -1,5 +1,5 @@ --- -"@udecode/plate-font": minor +'@udecode/plate-font': minor --- -add new transformas `setBlockBackgroundColor`. +Add new transforms `setBlockBackgroundColor`. diff --git a/.changeset/happy-masks-jump.md b/.changeset/happy-masks-jump.md index dd4b5341d8..1cfc8d64e6 100644 --- a/.changeset/happy-masks-jump.md +++ b/.changeset/happy-masks-jump.md @@ -1,5 +1,5 @@ --- -"@udecode/plate-layout": minor +'@udecode/plate-layout': minor --- -add `toggleColumns` and fix select all. +Add `toggleColumns` and fix select all. diff --git a/.changeset/lazy-poets-buy.md b/.changeset/lazy-poets-buy.md index 17a7df3ea9..9d7ab1809f 100644 --- a/.changeset/lazy-poets-buy.md +++ b/.changeset/lazy-poets-buy.md @@ -1,5 +1,5 @@ --- -"@udecode/plate-selection": minor +'@udecode/plate-selection': minor --- -Added logic for the `block-context-menu` and improved the user experience for `block-selection`, such as interactions related to keyboard shortcuts , bug fixes. +Add logic for the `block-context-menu` and improved the user experience for `block-selection`, such as interactions related to keyboard shortcuts, bug fixes. diff --git a/.changeset/long-lobsters-judge.md b/.changeset/long-lobsters-judge.md index ffe4cc69b3..e712d0dc02 100644 --- a/.changeset/long-lobsters-judge.md +++ b/.changeset/long-lobsters-judge.md @@ -1,5 +1,5 @@ --- -"@udecode/plate-heading": minor +'@udecode/plate-heading': minor --- -add `Toc Plugin` and `Toc Sidebar` +Add ToC plugin and sidebar diff --git a/.changeset/many-pillows-unite.md b/.changeset/many-pillows-unite.md index c70f383c3e..27c43f8fe2 100644 --- a/.changeset/many-pillows-unite.md +++ b/.changeset/many-pillows-unite.md @@ -1,5 +1,5 @@ --- -"@udecode/plate-math": minor +'@udecode/plate-math': minor --- -new plugins `equation` and `inlineEquation`. +Add `equation` and `inlineEquation` plugins. diff --git a/.changeset/ninety-knives-wash.md b/.changeset/ninety-knives-wash.md index 0065bfbdc3..f9c2c9bb31 100644 --- a/.changeset/ninety-knives-wash.md +++ b/.changeset/ninety-knives-wash.md @@ -1,5 +1,5 @@ --- -"@udecode/slate-utils": minor +'@udecode/slate-utils': minor --- -add new queryies utils `getAncestorNode` and `getNodesRange` +Add new queries `getAncestorNode` and `getNodesRange` diff --git a/.changeset/pink-yaks-smoke.md b/.changeset/pink-yaks-smoke.md index b60252cf02..22e9e190d1 100644 --- a/.changeset/pink-yaks-smoke.md +++ b/.changeset/pink-yaks-smoke.md @@ -1,5 +1,5 @@ --- -"@udecode/plate-dnd": minor +'@udecode/plate-dnd': minor --- -add selection after dragging ends. +Add selection after dragging ends. diff --git a/.changeset/smart-flies-relate.md b/.changeset/smart-flies-relate.md index ad7babdf7a..ffacb35fd0 100644 --- a/.changeset/smart-flies-relate.md +++ b/.changeset/smart-flies-relate.md @@ -1,5 +1,5 @@ --- -"@udecode/plate-callout": minor +'@udecode/plate-callout': minor --- -add new plugin `callout`. +Add new plugin `callout` diff --git a/.changeset/thin-cycles-itch.md b/.changeset/thin-cycles-itch.md index a9e2a6fbdb..aaa17060b0 100644 --- a/.changeset/thin-cycles-itch.md +++ b/.changeset/thin-cycles-itch.md @@ -1,5 +1,5 @@ --- -"@udecode/plate-indent-list": patch +'@udecode/plate-indent-list': patch --- -add `toggleIndentListByPath` ,`indentList` add at option +Add `toggleIndentListByPath`, add `indentList` options diff --git a/.changeset/wicked-cheetahs-laugh.md b/.changeset/wicked-cheetahs-laugh.md index 09e6c546ef..c502b83ae2 100644 --- a/.changeset/wicked-cheetahs-laugh.md +++ b/.changeset/wicked-cheetahs-laugh.md @@ -1,5 +1,5 @@ --- -"@udecode/plate-utils": minor +'@udecode/plate-utils': minor --- -add `addSelectedRow` which depend on blockSelection plugin +Add `addSelectedRow` which depends on `blockSelection` plugin diff --git a/.changeset/wise-pandas-thank.md b/.changeset/wise-pandas-thank.md index 8057cbd0ab..c2692fc4c6 100644 --- a/.changeset/wise-pandas-thank.md +++ b/.changeset/wise-pandas-thank.md @@ -1,5 +1,5 @@ --- -"@udecode/plate-link": patch +'@udecode/plate-link': patch --- -fix link button make floating toolbar close. +Fix link button closing the floating toolbar. diff --git a/.changeset/yellow-gifts-vanish.md b/.changeset/yellow-gifts-vanish.md index 0d26c31710..474094dd90 100644 --- a/.changeset/yellow-gifts-vanish.md +++ b/.changeset/yellow-gifts-vanish.md @@ -1,5 +1,5 @@ --- -"@udecode/plate-core": patch +'@udecode/plate-core': patch --- -Fix the issue where the `toggleNodeType` function does not work when passing `at`. +Fix: `toggleNodeType` not working using `at`. diff --git a/.changeset/young-pigs-behave.md b/.changeset/young-pigs-behave.md index a49cbea8ee..b4dd34f2ef 100644 --- a/.changeset/young-pigs-behave.md +++ b/.changeset/young-pigs-behave.md @@ -1,5 +1,5 @@ --- -"@udecode/plate-media": minor +'@udecode/plate-media': minor --- -add plugins `mediaPlaceholder`, `video`,`audio` and `file`. +Add plugins: `mediaPlaceholder`, `video`,`audio` and `file` diff --git a/.changeset/young-sheep-rush.md b/.changeset/young-sheep-rush.md index ec09245b3d..b29ec61933 100644 --- a/.changeset/young-sheep-rush.md +++ b/.changeset/young-sheep-rush.md @@ -1,5 +1,5 @@ --- -"@udecode/plate-code-block": patch +'@udecode/plate-code-block': patch --- -fix selection all +Fix select all From 46bfe03cd382ecbeab37e07b4b5cfecfc2cffba8 Mon Sep 17 00:00:00 2001 From: FelixFeng Date: Thu, 6 Jun 2024 19:06:53 +0800 Subject: [PATCH 48/63] fix: right-click --- apps/www/src/registry/default/example/playground-demo.tsx | 1 + packages/selection/src/components/BlockSelectable.tsx | 7 +++++++ packages/selection/src/createBlockSelectionPlugin.tsx | 1 + 3 files changed, 9 insertions(+) diff --git a/apps/www/src/registry/default/example/playground-demo.tsx b/apps/www/src/registry/default/example/playground-demo.tsx index a5ca9d4178..56b1e5f81b 100644 --- a/apps/www/src/registry/default/example/playground-demo.tsx +++ b/apps/www/src/registry/default/example/playground-demo.tsx @@ -272,6 +272,7 @@ export const usePlaygroundPlugins = ({ createBlockSelectionPlugin({ enabled: id === 'blockselection' || !!enabled.blockSelection, options: { + disableContextMenu: true, sizes: { bottom: 0, top: 0, diff --git a/packages/selection/src/components/BlockSelectable.tsx b/packages/selection/src/components/BlockSelectable.tsx index cbbed72496..43543746c7 100644 --- a/packages/selection/src/components/BlockSelectable.tsx +++ b/packages/selection/src/components/BlockSelectable.tsx @@ -88,6 +88,13 @@ export const useBlockSelectable = ({ key: id, onContextMenu: (event: React.MouseEvent) => { if (!editor) return; + + const { disableContextMenu } = getPluginOptions( + editor, + KEY_BLOCK_SELECTION + ); + + if (disableContextMenu) return; if (editor.selection?.focus) { const nodeEntry = getAboveNode(editor); diff --git a/packages/selection/src/createBlockSelectionPlugin.tsx b/packages/selection/src/createBlockSelectionPlugin.tsx index eb0bf84570..6402d38c81 100644 --- a/packages/selection/src/createBlockSelectionPlugin.tsx +++ b/packages/selection/src/createBlockSelectionPlugin.tsx @@ -15,6 +15,7 @@ import { withSelection } from './withSelection'; export const KEY_BLOCK_SELECTION = 'blockSelection'; export interface BlockSelectionPlugin { + disableContextMenu?: boolean; onKeyDownSelecting?: (e: KeyboardEvent) => void; query?: QueryNodeOptions; sizes?: { From 12905bfee77b512da3dd13ac390e2f72a7e68f9e Mon Sep 17 00:00:00 2001 From: zbeyens Date: Thu, 6 Jun 2024 13:33:25 +0200 Subject: [PATCH 49/63] doc --- .changeset/blue-readers-judge.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/blue-readers-judge.md b/.changeset/blue-readers-judge.md index f0dc43271a..9ca5eb939c 100644 --- a/.changeset/blue-readers-judge.md +++ b/.changeset/blue-readers-judge.md @@ -1,5 +1,5 @@ --- -"@udecode/plate-caption": major +'@udecode/plate-caption': major --- -The show of the caption needs to be controlled through `showCaption`. +- Breaking change: the empty caption textarea is no longer showed on image focus. You can use the caption store to show the caption textarea. From 641e8d157198d8112129ed6117d137ed8150010b Mon Sep 17 00:00:00 2001 From: FelixFeng Date: Thu, 6 Jun 2024 19:43:29 +0800 Subject: [PATCH 50/63] chore: set default value of `disableContextMenu` to true --- packages/selection/src/components/BlockSelectable.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/selection/src/components/BlockSelectable.tsx b/packages/selection/src/components/BlockSelectable.tsx index 43543746c7..9a57adb810 100644 --- a/packages/selection/src/components/BlockSelectable.tsx +++ b/packages/selection/src/components/BlockSelectable.tsx @@ -89,10 +89,8 @@ export const useBlockSelectable = ({ onContextMenu: (event: React.MouseEvent) => { if (!editor) return; - const { disableContextMenu } = getPluginOptions( - editor, - KEY_BLOCK_SELECTION - ); + const { disableContextMenu = true } = + getPluginOptions(editor, KEY_BLOCK_SELECTION); if (disableContextMenu) return; if (editor.selection?.focus) { From be15a00db348a3d694c896fce435894d02f4b51a Mon Sep 17 00:00:00 2001 From: FelixFeng Date: Thu, 6 Jun 2024 21:31:41 +0800 Subject: [PATCH 51/63] fix --- .../src/registry/default/plate-ui/caption.tsx | 16 ++- .../default/plate-ui/image-element.tsx | 111 ++++++++++-------- .../default/plate-ui/media-popover.tsx | 3 + packages/caption/src/TCaptionElement.ts | 1 - packages/caption/src/components/Caption.tsx | 7 +- .../caption/src/components/CaptionButton.tsx | 30 +++++ packages/caption/src/components/index.ts | 1 + packages/caption/src/index.ts | 1 + packages/caption/src/useResizableStore.ts | 13 ++ packages/media/src/media/index.ts | 1 + packages/media/src/media/mediaStore.ts | 12 ++ 11 files changed, 140 insertions(+), 56 deletions(-) create mode 100644 packages/caption/src/components/CaptionButton.tsx create mode 100644 packages/caption/src/useResizableStore.ts create mode 100644 packages/media/src/media/mediaStore.ts diff --git a/apps/www/src/registry/default/plate-ui/caption.tsx b/apps/www/src/registry/default/plate-ui/caption.tsx index 6ca40e099b..bbd5ec41e1 100644 --- a/apps/www/src/registry/default/plate-ui/caption.tsx +++ b/apps/www/src/registry/default/plate-ui/caption.tsx @@ -1,10 +1,19 @@ -import { cn, withCn, withVariants } from '@udecode/cn'; +import { + cn, + createPrimitiveComponent, + withCn, + withVariants, +} from '@udecode/cn'; import { Caption as CaptionPrimitive, CaptionTextarea as CaptionTextareaPrimitive, + useCaptionButton, + useCaptionButtonState, } from '@udecode/plate-caption'; import { cva } from 'class-variance-authority'; +import { Button } from './button'; + const captionVariants = cva('max-w-full', { defaultVariants: { align: 'center', @@ -30,3 +39,8 @@ export const CaptionTextarea = withCn( 'text-center print:placeholder:text-transparent' ) ); + +export const CaptionButton = createPrimitiveComponent(Button)({ + propsHook: useCaptionButton, + stateHook: useCaptionButtonState, +}); diff --git a/apps/www/src/registry/default/plate-ui/image-element.tsx b/apps/www/src/registry/default/plate-ui/image-element.tsx index 069927975a..037a2f0be6 100644 --- a/apps/www/src/registry/default/plate-ui/image-element.tsx +++ b/apps/www/src/registry/default/plate-ui/image-element.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { cn, withRef } from '@udecode/cn'; +import { CaptionProvider } from '@udecode/plate-caption'; import { PlateElement, withHOC } from '@udecode/plate-common'; import { ELEMENT_IMAGE, Image, useMediaState } from '@udecode/plate-media'; import { ResizableProvider, useResizableStore } from '@udecode/plate-resizable'; @@ -14,59 +15,69 @@ import { } from './resizable'; export const ImageElement = withHOC( - ResizableProvider, - withRef( - ({ children, className, nodeProps, ...props }, ref) => { - const { align = 'center', focused, readOnly, selected } = useMediaState(); + CaptionProvider, + withHOC( + ResizableProvider, + withRef( + ({ children, className, nodeProps, ...props }, ref) => { + const { + align = 'center', + focused, + readOnly, + selected, + } = useMediaState(); - const width = useResizableStore().get.width(); + const width = useResizableStore().get.width(); - return ( - - -
- - - - - + return ( + + +
+ + + + + - - - -
+ + + +
- {children} -
-
- ); - } + {children} + + + ); + } + ) ) ); diff --git a/apps/www/src/registry/default/plate-ui/media-popover.tsx b/apps/www/src/registry/default/plate-ui/media-popover.tsx index 2e5cd23a41..465c1fa5f5 100644 --- a/apps/www/src/registry/default/plate-ui/media-popover.tsx +++ b/apps/www/src/registry/default/plate-ui/media-popover.tsx @@ -16,6 +16,7 @@ import { useReadOnly, useSelected } from 'slate-react'; import { Icons } from '@/components/icons'; import { Button, buttonVariants } from './button'; +import { CaptionButton } from './caption'; import { inputVariants } from './input'; import { Popover, PopoverAnchor, PopoverContent } from './popover'; import { Separator } from './separator'; @@ -80,6 +81,8 @@ export function MediaPopover({ children, pluginKey }: MediaPopoverProps) { Edit link + Show caption +