From acb0b91578f92a62e6cae493aa0c1ef98687dccb Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Wed, 1 May 2024 08:30:56 +0100 Subject: [PATCH 01/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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 8cf1b3d87c0396fbb60e678a9087c04ea62d19bf Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Mon, 3 Jun 2024 17:10:49 +0100 Subject: [PATCH 31/37] 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 32/37] 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 33/37] 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 34/37] 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 35/37] 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 36/37] 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 37/37] 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