@prosekit/extensions 0.11.3 → 0.11.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commit/style.css +2 -0
- package/dist/commit/style.css.map +1 -0
- package/dist/commit/style.js +1 -0
- package/dist/{drop-indicator-B8P652g2.js → drop-indicator-D1eHOhSi.js} +6 -5
- package/dist/drop-indicator-D1eHOhSi.js.map +1 -0
- package/dist/enter-rule-RdhEA900.js +2 -1
- package/dist/enter-rule-RdhEA900.js.map +1 -0
- package/dist/gap-cursor/style.css +2 -0
- package/dist/gap-cursor/style.css.map +1 -0
- package/dist/gap-cursor/style.js +1 -0
- package/dist/{input-rule-Gji4N7Oe.js → input-rule-B17tpW4m.js} +3 -3
- package/dist/input-rule-B17tpW4m.js.map +1 -0
- package/dist/list/style.css +2 -0
- package/dist/list/style.css.map +1 -0
- package/dist/list/style.js +1 -0
- package/dist/loro/style.css +2 -0
- package/dist/loro/style.css.map +1 -0
- package/dist/loro/style.js +1 -0
- package/dist/{mark-rule-D7zaa32n.js → mark-rule-BCqIZMDu.js} +3 -3
- package/dist/mark-rule-BCqIZMDu.js.map +1 -0
- package/dist/{paste-rule-Cca3n5TA.js → paste-rule-DIEJKIje.js} +5 -15
- package/dist/paste-rule-DIEJKIje.js.map +1 -0
- package/dist/placeholder/style.css +2 -0
- package/dist/placeholder/style.css.map +1 -0
- package/dist/placeholder/style.js +1 -0
- package/dist/prosekit-extensions-autocomplete.d.ts +2 -1
- package/dist/prosekit-extensions-autocomplete.d.ts.map +1 -0
- package/dist/prosekit-extensions-autocomplete.js +2 -9
- package/dist/prosekit-extensions-autocomplete.js.map +1 -0
- package/dist/prosekit-extensions-blockquote.d.ts +2 -1
- package/dist/prosekit-extensions-blockquote.d.ts.map +1 -0
- package/dist/prosekit-extensions-blockquote.js +4 -4
- package/dist/prosekit-extensions-blockquote.js.map +1 -0
- package/dist/prosekit-extensions-bold.d.ts +2 -1
- package/dist/prosekit-extensions-bold.d.ts.map +1 -0
- package/dist/prosekit-extensions-bold.js +3 -2
- package/dist/prosekit-extensions-bold.js.map +1 -0
- package/dist/prosekit-extensions-code-block.d.ts +2 -1
- package/dist/prosekit-extensions-code-block.d.ts.map +1 -0
- package/dist/prosekit-extensions-code-block.js +5 -6
- package/dist/prosekit-extensions-code-block.js.map +1 -0
- package/dist/prosekit-extensions-code.d.ts +2 -1
- package/dist/prosekit-extensions-code.d.ts.map +1 -0
- package/dist/prosekit-extensions-code.js +3 -2
- package/dist/prosekit-extensions-code.js.map +1 -0
- package/dist/prosekit-extensions-commit.d.ts +2 -1
- package/dist/prosekit-extensions-commit.d.ts.map +1 -0
- package/dist/prosekit-extensions-commit.js +11 -13
- package/dist/prosekit-extensions-commit.js.map +1 -0
- package/dist/prosekit-extensions-doc.d.ts +2 -1
- package/dist/prosekit-extensions-doc.d.ts.map +1 -0
- package/dist/prosekit-extensions-doc.js +2 -1
- package/dist/prosekit-extensions-doc.js.map +1 -0
- package/dist/prosekit-extensions-drop-cursor.d.ts +2 -1
- package/dist/prosekit-extensions-drop-cursor.d.ts.map +1 -0
- package/dist/prosekit-extensions-drop-cursor.js +2 -1
- package/dist/prosekit-extensions-drop-cursor.js.map +1 -0
- package/dist/prosekit-extensions-drop-indicator.d.ts +2 -1
- package/dist/prosekit-extensions-drop-indicator.d.ts.map +1 -0
- package/dist/prosekit-extensions-drop-indicator.js +1 -1
- package/dist/prosekit-extensions-enter-rule.d.ts +2 -1
- package/dist/prosekit-extensions-enter-rule.d.ts.map +1 -0
- package/dist/prosekit-extensions-file.d.ts +2 -1
- package/dist/prosekit-extensions-file.d.ts.map +1 -0
- package/dist/prosekit-extensions-file.js +4 -16
- package/dist/prosekit-extensions-file.js.map +1 -0
- package/dist/prosekit-extensions-gap-cursor.d.ts +2 -2
- package/dist/prosekit-extensions-gap-cursor.d.ts.map +1 -0
- package/dist/prosekit-extensions-gap-cursor.js +2 -1
- package/dist/prosekit-extensions-gap-cursor.js.map +1 -0
- package/dist/prosekit-extensions-hard-break.d.ts +2 -1
- package/dist/prosekit-extensions-hard-break.d.ts.map +1 -0
- package/dist/prosekit-extensions-hard-break.js +2 -1
- package/dist/prosekit-extensions-hard-break.js.map +1 -0
- package/dist/prosekit-extensions-heading.d.ts +2 -1
- package/dist/prosekit-extensions-heading.d.ts.map +1 -0
- package/dist/prosekit-extensions-heading.js +5 -6
- package/dist/prosekit-extensions-heading.js.map +1 -0
- package/dist/prosekit-extensions-horizontal-rule.d.ts +2 -1
- package/dist/prosekit-extensions-horizontal-rule.d.ts.map +1 -0
- package/dist/prosekit-extensions-horizontal-rule.js +5 -6
- package/dist/prosekit-extensions-horizontal-rule.js.map +1 -0
- package/dist/prosekit-extensions-image.d.ts +2 -1
- package/dist/prosekit-extensions-image.d.ts.map +1 -0
- package/dist/prosekit-extensions-image.js +3 -3
- package/dist/prosekit-extensions-image.js.map +1 -0
- package/dist/prosekit-extensions-input-rule.d.ts +2 -1
- package/dist/prosekit-extensions-input-rule.d.ts.map +1 -0
- package/dist/prosekit-extensions-input-rule.js +1 -1
- package/dist/prosekit-extensions-italic.d.ts +2 -1
- package/dist/prosekit-extensions-italic.d.ts.map +1 -0
- package/dist/prosekit-extensions-italic.js +3 -2
- package/dist/prosekit-extensions-italic.js.map +1 -0
- package/dist/prosekit-extensions-link.d.ts +2 -1
- package/dist/prosekit-extensions-link.d.ts.map +1 -0
- package/dist/prosekit-extensions-link.js +6 -5
- package/dist/prosekit-extensions-link.js.map +1 -0
- package/dist/prosekit-extensions-list.d.ts +22 -21
- package/dist/prosekit-extensions-list.d.ts.map +1 -0
- package/dist/prosekit-extensions-list.js +6 -7
- package/dist/prosekit-extensions-list.js.map +1 -0
- package/dist/prosekit-extensions-loro.d.ts +14 -13
- package/dist/prosekit-extensions-loro.d.ts.map +1 -0
- package/dist/prosekit-extensions-loro.js +2 -1
- package/dist/prosekit-extensions-loro.js.map +1 -0
- package/dist/prosekit-extensions-mark-rule.d.ts +2 -1
- package/dist/prosekit-extensions-mark-rule.d.ts.map +1 -0
- package/dist/prosekit-extensions-mark-rule.js +1 -1
- package/dist/prosekit-extensions-mention.d.ts +2 -1
- package/dist/prosekit-extensions-mention.d.ts.map +1 -0
- package/dist/prosekit-extensions-mention.js +2 -1
- package/dist/prosekit-extensions-mention.js.map +1 -0
- package/dist/prosekit-extensions-mod-click-prevention.d.ts +2 -1
- package/dist/prosekit-extensions-mod-click-prevention.d.ts.map +1 -0
- package/dist/prosekit-extensions-mod-click-prevention.js +2 -1
- package/dist/prosekit-extensions-mod-click-prevention.js.map +1 -0
- package/dist/prosekit-extensions-paragraph.d.ts +2 -1
- package/dist/prosekit-extensions-paragraph.d.ts.map +1 -0
- package/dist/prosekit-extensions-paragraph.js +2 -1
- package/dist/prosekit-extensions-paragraph.js.map +1 -0
- package/dist/prosekit-extensions-paste-rule.d.ts +2 -1
- package/dist/prosekit-extensions-paste-rule.d.ts.map +1 -0
- package/dist/prosekit-extensions-paste-rule.js +1 -1
- package/dist/prosekit-extensions-placeholder.d.ts +2 -1
- package/dist/prosekit-extensions-placeholder.d.ts.map +1 -0
- package/dist/prosekit-extensions-placeholder.js +5 -4
- package/dist/prosekit-extensions-placeholder.js.map +1 -0
- package/dist/prosekit-extensions-readonly.d.ts +2 -1
- package/dist/prosekit-extensions-readonly.d.ts.map +1 -0
- package/dist/prosekit-extensions-readonly.js +2 -1
- package/dist/prosekit-extensions-readonly.js.map +1 -0
- package/dist/prosekit-extensions-search.d.ts +2 -1
- package/dist/prosekit-extensions-search.d.ts.map +1 -0
- package/dist/prosekit-extensions-search.js +3 -3
- package/dist/prosekit-extensions-search.js.map +1 -0
- package/dist/prosekit-extensions-strike.d.ts +2 -1
- package/dist/prosekit-extensions-strike.d.ts.map +1 -0
- package/dist/prosekit-extensions-strike.js +3 -2
- package/dist/prosekit-extensions-strike.js.map +1 -0
- package/dist/prosekit-extensions-table.d.ts +56 -114
- package/dist/prosekit-extensions-table.d.ts.map +1 -0
- package/dist/prosekit-extensions-table.js +3 -2
- package/dist/prosekit-extensions-text-align.d.ts +2 -1
- package/dist/prosekit-extensions-text-align.d.ts.map +1 -0
- package/dist/prosekit-extensions-text-align.js +2 -1
- package/dist/prosekit-extensions-text-align.js.map +1 -0
- package/dist/prosekit-extensions-text.d.ts +2 -1
- package/dist/prosekit-extensions-text.d.ts.map +1 -0
- package/dist/prosekit-extensions-text.js +2 -1
- package/dist/prosekit-extensions-text.js.map +1 -0
- package/dist/prosekit-extensions-underline.d.ts +2 -1
- package/dist/prosekit-extensions-underline.d.ts.map +1 -0
- package/dist/prosekit-extensions-underline.js +2 -1
- package/dist/prosekit-extensions-underline.js.map +1 -0
- package/dist/prosekit-extensions-virtual-selection.d.ts +2 -1
- package/dist/prosekit-extensions-virtual-selection.d.ts.map +1 -0
- package/dist/prosekit-extensions-virtual-selection.js +3 -3
- package/dist/prosekit-extensions-virtual-selection.js.map +1 -0
- package/dist/prosekit-extensions-yjs.d.ts +2 -1
- package/dist/prosekit-extensions-yjs.d.ts.map +1 -0
- package/dist/prosekit-extensions-yjs.js +2 -1
- package/dist/prosekit-extensions-yjs.js.map +1 -0
- package/dist/prosekit-extensions.d.ts +1 -1
- package/dist/prosekit-extensions.js +1 -0
- package/dist/search/style.css +2 -0
- package/dist/search/style.css.map +1 -0
- package/dist/search/style.js +1 -0
- package/dist/shiki-highlighter-chunk-DSPM0T27.d.ts +2 -1
- package/dist/shiki-highlighter-chunk-DSPM0T27.d.ts.map +1 -0
- package/dist/shiki-highlighter-chunk.js +3 -5
- package/dist/shiki-highlighter-chunk.js.map +1 -0
- package/dist/table/style.css +2 -0
- package/dist/table/style.css.map +1 -0
- package/dist/table/style.js +1 -0
- package/dist/table-Bi7WsMI3.js +297 -0
- package/dist/table-Bi7WsMI3.js.map +1 -0
- package/dist/virtual-selection/style.css +2 -0
- package/dist/virtual-selection/style.css.map +1 -0
- package/dist/virtual-selection/style.js +1 -0
- package/dist/yjs/style.css +2 -0
- package/dist/yjs/style.css.map +1 -0
- package/dist/yjs/style.js +1 -0
- package/package.json +10 -9
- package/src/autocomplete/autocomplete-helpers.ts +74 -0
- package/src/autocomplete/autocomplete-plugin.ts +186 -0
- package/src/autocomplete/autocomplete-rule.ts +117 -0
- package/src/autocomplete/autocomplete.spec.ts +132 -0
- package/src/autocomplete/autocomplete.ts +29 -0
- package/src/autocomplete/index.ts +9 -0
- package/src/blockquote/blockquote-commands.ts +32 -0
- package/src/blockquote/blockquote-input-rule.ts +14 -0
- package/src/blockquote/blockquote-keymap.spec.ts +45 -0
- package/src/blockquote/blockquote-keymap.ts +31 -0
- package/src/blockquote/blockquote-spec.ts +24 -0
- package/src/blockquote/blockquote.ts +34 -0
- package/src/blockquote/index.ts +14 -0
- package/src/bold/bold-commands.ts +23 -0
- package/src/bold/bold-input-rule.spec.ts +51 -0
- package/src/bold/bold-input-rule.ts +18 -0
- package/src/bold/bold-keymap.ts +14 -0
- package/src/bold/bold-spec.ts +53 -0
- package/src/bold/bold.ts +32 -0
- package/src/bold/index.ts +14 -0
- package/src/code/code-commands.ts +23 -0
- package/src/code/code-input-rule.ts +18 -0
- package/src/code/code-keymap.ts +14 -0
- package/src/code/code-spec.ts +28 -0
- package/src/code/code.ts +32 -0
- package/src/code/index.ts +14 -0
- package/src/code-block/code-block-commands.ts +44 -0
- package/src/code-block/code-block-highlight.ts +40 -0
- package/src/code-block/code-block-input-rule.ts +36 -0
- package/src/code-block/code-block-keymap.ts +61 -0
- package/src/code-block/code-block-shiki.ts +58 -0
- package/src/code-block/code-block-spec.spec.ts +164 -0
- package/src/code-block/code-block-spec.ts +71 -0
- package/src/code-block/code-block-types.ts +8 -0
- package/src/code-block/code-block.ts +46 -0
- package/src/code-block/index.ts +32 -0
- package/src/code-block/shiki-bundle.ts +8 -0
- package/src/code-block/shiki-highlighter-chunk.ts +84 -0
- package/src/code-block/shiki-highlighter.ts +22 -0
- package/src/code-block/shiki-parser.ts +36 -0
- package/src/commit/index.ts +330 -0
- package/src/commit/style.css +7 -0
- package/src/doc/index.ts +21 -0
- package/src/drop-cursor/drop-cursor.ts +46 -0
- package/src/drop-cursor/index.ts +5 -0
- package/src/drop-indicator/drop-indicator-facet.ts +84 -0
- package/src/drop-indicator/drop-indicator-plugin.ts +147 -0
- package/src/drop-indicator/drop-indicator.ts +37 -0
- package/src/drop-indicator/drop-target.ts +168 -0
- package/src/drop-indicator/index.ts +14 -0
- package/src/drop-indicator/types.ts +90 -0
- package/src/enter-rule/index.ts +241 -0
- package/src/file/file-drop-handler.ts +75 -0
- package/src/file/file-paste-handler.spec.ts +95 -0
- package/src/file/file-paste-handler.ts +59 -0
- package/src/file/file-upload.ts +119 -0
- package/src/file/helpers.ts +39 -0
- package/src/file/index.ts +16 -0
- package/src/gap-cursor/gap-cursor.ts +28 -0
- package/src/gap-cursor/index.ts +4 -0
- package/src/gap-cursor/style.css +25 -0
- package/src/hard-break/hard-break-commands.ts +31 -0
- package/src/hard-break/hard-break-keymap.spec.ts +45 -0
- package/src/hard-break/hard-break-keymap.ts +16 -0
- package/src/hard-break/hard-break-spec.ts +31 -0
- package/src/hard-break/hard-break.ts +32 -0
- package/src/hard-break/index.ts +13 -0
- package/src/heading/heading-commands.ts +37 -0
- package/src/heading/heading-input-rule.ts +22 -0
- package/src/heading/heading-keymap.spec.ts +53 -0
- package/src/heading/heading-keymap.ts +40 -0
- package/src/heading/heading-spec.ts +39 -0
- package/src/heading/heading-types.ts +3 -0
- package/src/heading/heading.ts +34 -0
- package/src/heading/index.ts +15 -0
- package/src/horizontal-rule/horizontal-rule-commands.spec.ts +61 -0
- package/src/horizontal-rule/horizontal-rule-commands.ts +37 -0
- package/src/horizontal-rule/horizontal-rule-input-rule.spec.ts +61 -0
- package/src/horizontal-rule/horizontal-rule-input-rule.ts +26 -0
- package/src/horizontal-rule/horizontal-rule-spec.ts +21 -0
- package/src/horizontal-rule/horizontal-rule.ts +29 -0
- package/src/horizontal-rule/index.ts +14 -0
- package/src/image/image-commands.ts +27 -0
- package/src/image/image-spec.ts +72 -0
- package/src/image/image.ts +25 -0
- package/src/image/index.ts +13 -0
- package/src/index.ts +1 -0
- package/src/input-rule/index.ts +237 -0
- package/src/italic/index.ts +14 -0
- package/src/italic/italic-commands.spec.ts +75 -0
- package/src/italic/italic-commands.ts +23 -0
- package/src/italic/italic-input-rule.spec.ts +25 -0
- package/src/italic/italic-input-rule.ts +18 -0
- package/src/italic/italic-keymap.ts +14 -0
- package/src/italic/italic-spec.ts +35 -0
- package/src/italic/italic.ts +34 -0
- package/src/link/index.spec.ts +88 -0
- package/src/link/index.ts +156 -0
- package/src/link/link-paste-rule.spec.ts +194 -0
- package/src/link/link-paste-rule.ts +22 -0
- package/src/link/link-regex.spec.ts +82 -0
- package/src/link/link-regex.ts +79 -0
- package/src/link/link-types.ts +8 -0
- package/src/list/index.ts +25 -0
- package/src/list/list-commands.ts +61 -0
- package/src/list/list-drop-indicator.ts +37 -0
- package/src/list/list-input-rules.ts +14 -0
- package/src/list/list-keymap.spec.ts +39 -0
- package/src/list/list-keymap.ts +48 -0
- package/src/list/list-plugins.ts +35 -0
- package/src/list/list-serializer.ts +38 -0
- package/src/list/list-spec.ts +60 -0
- package/src/list/list-types.spec.ts +10 -0
- package/src/list/list-types.ts +23 -0
- package/src/list/list.spec.ts +134 -0
- package/src/list/list.ts +38 -0
- package/src/list/style.css +128 -0
- package/src/loro/index.ts +17 -0
- package/src/loro/loro-commands.ts +27 -0
- package/src/loro/loro-cursor-plugin.ts +28 -0
- package/src/loro/loro-keymap.ts +23 -0
- package/src/loro/loro-sync-plugin.ts +14 -0
- package/src/loro/loro-undo-plugin.ts +12 -0
- package/src/loro/loro.ts +75 -0
- package/src/loro/style.css +33 -0
- package/src/mark-rule/apply.ts +129 -0
- package/src/mark-rule/index.ts +2 -0
- package/src/mark-rule/mark-rule.spec.ts +123 -0
- package/src/mark-rule/mark-rule.ts +48 -0
- package/src/mark-rule/range.ts +107 -0
- package/src/mark-rule/types.ts +30 -0
- package/src/mention/index.ts +90 -0
- package/src/mod-click-prevention/index.ts +35 -0
- package/src/paragraph/index.ts +7 -0
- package/src/paragraph/paragraph-commands.ts +29 -0
- package/src/paragraph/paragraph-keymap.ts +15 -0
- package/src/paragraph/paragraph-spec.ts +31 -0
- package/src/paragraph/paragraph.ts +37 -0
- package/src/paste-rule/index.ts +10 -0
- package/src/paste-rule/mark-paste-rule.spec.ts +112 -0
- package/src/paste-rule/mark-paste-rule.ts +194 -0
- package/src/paste-rule/paste-rule-plugin.ts +53 -0
- package/src/paste-rule/paste-rule.spec.ts +96 -0
- package/src/paste-rule/paste-rule.ts +60 -0
- package/src/paste-rule/split-text-by-regex.spec.ts +97 -0
- package/src/paste-rule/split-text-by-regex.ts +44 -0
- package/src/placeholder/index.ts +113 -0
- package/src/placeholder/style.css +7 -0
- package/src/readonly/index.ts +22 -0
- package/src/search/index.ts +140 -0
- package/src/search/style.css +13 -0
- package/src/strike/index.ts +101 -0
- package/src/table/index.ts +53 -0
- package/src/table/style.css +42 -0
- package/src/table/table-commands/delete-cell-selection.spec.ts +41 -0
- package/src/table/table-commands/delete-cell-selection.ts +1 -0
- package/src/table/table-commands/exit-table.spec.ts +45 -0
- package/src/table/table-commands/exit-table.ts +49 -0
- package/src/table/table-commands/insert-table.spec.ts +39 -0
- package/src/table/table-commands/insert-table.ts +80 -0
- package/src/table/table-commands/move-table-column.spec.ts +618 -0
- package/src/table/table-commands/move-table-column.ts +4 -0
- package/src/table/table-commands/move-table-row.spec.ts +380 -0
- package/src/table/table-commands/move-table-row.ts +4 -0
- package/src/table/table-commands/select-table-cell.spec.ts +34 -0
- package/src/table/table-commands/select-table-cell.ts +35 -0
- package/src/table/table-commands/select-table-column.spec.ts +33 -0
- package/src/table/table-commands/select-table-column.ts +39 -0
- package/src/table/table-commands/select-table-row.spec.ts +32 -0
- package/src/table/table-commands/select-table-row.ts +39 -0
- package/src/table/table-commands/select-table.spec.ts +36 -0
- package/src/table/table-commands/select-table.ts +50 -0
- package/src/table/table-commands.ts +110 -0
- package/src/table/table-drop-indicator.ts +40 -0
- package/src/table/table-plugins.ts +15 -0
- package/src/table/table-spec.spec.ts +113 -0
- package/src/table/table-spec.ts +109 -0
- package/src/table/table-utils.ts +16 -0
- package/src/table/table.ts +49 -0
- package/src/table/test-utils.ts +28 -0
- package/src/testing/clipboard.ts +58 -0
- package/src/testing/format-html.ts +5 -0
- package/src/testing/index.ts +161 -0
- package/src/testing/keyboard.ts +36 -0
- package/src/testing/markdown.ts +23 -0
- package/src/text/index.ts +24 -0
- package/src/text-align/index.ts +133 -0
- package/src/types/assert-type-equal.ts +8 -0
- package/src/underline/index.ts +83 -0
- package/src/virtual-selection/index.ts +100 -0
- package/src/virtual-selection/style.css +5 -0
- package/src/yjs/index.ts +22 -0
- package/src/yjs/style.css +31 -0
- package/src/yjs/yjs-commands.ts +27 -0
- package/src/yjs/yjs-cursor-plugin.ts +25 -0
- package/src/yjs/yjs-keymap.ts +23 -0
- package/src/yjs/yjs-sync-plugin.ts +23 -0
- package/src/yjs/yjs-undo-plugin.ts +87 -0
- package/src/yjs/yjs.ts +84 -0
- package/dist/table-C_qAMj5-.js +0 -734
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import type { ResolvedPos } from '@prosekit/pm/model'
|
|
2
|
+
import {
|
|
3
|
+
NodeSelection,
|
|
4
|
+
Plugin,
|
|
5
|
+
PluginKey,
|
|
6
|
+
TextSelection,
|
|
7
|
+
type PluginView,
|
|
8
|
+
} from '@prosekit/pm/state'
|
|
9
|
+
import type { EditorView } from '@prosekit/pm/view'
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
buildGetTarget,
|
|
13
|
+
type GetTarget,
|
|
14
|
+
} from './drop-target'
|
|
15
|
+
import type {
|
|
16
|
+
DragEventHandler,
|
|
17
|
+
ShowHandler,
|
|
18
|
+
ViewDragging,
|
|
19
|
+
} from './types'
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
interface DropIndicatorPluginOptions {
|
|
25
|
+
onDrag: DragEventHandler
|
|
26
|
+
onShow: ShowHandler
|
|
27
|
+
onHide: VoidFunction
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @internal
|
|
32
|
+
*/
|
|
33
|
+
export function createDropIndicatorPlugin(options: DropIndicatorPluginOptions): Plugin {
|
|
34
|
+
let getTarget: GetTarget | undefined
|
|
35
|
+
|
|
36
|
+
return new Plugin({
|
|
37
|
+
key: new PluginKey('prosekit-drop-indicator'),
|
|
38
|
+
view: (view) => {
|
|
39
|
+
getTarget = buildGetTarget(view, options.onDrag)
|
|
40
|
+
return createDropIndicatorView(view, getTarget, options)
|
|
41
|
+
},
|
|
42
|
+
props: {
|
|
43
|
+
handleDrop(view, event, slice, move): boolean {
|
|
44
|
+
if (!getTarget) return false
|
|
45
|
+
|
|
46
|
+
const target = getTarget([event.clientX, event.clientY], event)
|
|
47
|
+
|
|
48
|
+
if (!target) return false
|
|
49
|
+
|
|
50
|
+
event.preventDefault()
|
|
51
|
+
let insertPos = target[0]
|
|
52
|
+
|
|
53
|
+
let tr = view.state.tr
|
|
54
|
+
if (move) {
|
|
55
|
+
let { node } = (view.dragging as ViewDragging | null) || {}
|
|
56
|
+
if (node) node.replace(tr)
|
|
57
|
+
else tr.deleteSelection()
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let pos = tr.mapping.map(insertPos)
|
|
61
|
+
let isNode = slice.openStart == 0 && slice.openEnd == 0 && slice.content.childCount == 1
|
|
62
|
+
let beforeInsert = tr.doc
|
|
63
|
+
if (isNode) tr.replaceRangeWith(pos, pos, slice.content.firstChild!)
|
|
64
|
+
else tr.replaceRange(pos, pos, slice)
|
|
65
|
+
if (tr.doc.eq(beforeInsert)) {
|
|
66
|
+
return true
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let $pos = tr.doc.resolve(pos)
|
|
70
|
+
if (
|
|
71
|
+
isNode && NodeSelection.isSelectable(slice.content.firstChild!)
|
|
72
|
+
&& $pos.nodeAfter && $pos.nodeAfter.sameMarkup(slice.content.firstChild!)
|
|
73
|
+
) {
|
|
74
|
+
tr.setSelection(new NodeSelection($pos))
|
|
75
|
+
} else {
|
|
76
|
+
let end = tr.mapping.map(insertPos)
|
|
77
|
+
tr.mapping.maps[tr.mapping.maps.length - 1].forEach((_from, _to, _newFrom, newTo) => end = newTo)
|
|
78
|
+
tr.setSelection(selectionBetween(view, $pos, tr.doc.resolve(end)))
|
|
79
|
+
}
|
|
80
|
+
view.focus()
|
|
81
|
+
view.dispatch(tr.setMeta('uiEvent', 'drop'))
|
|
82
|
+
return true
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function selectionBetween(view: EditorView, $anchor: ResolvedPos, $head: ResolvedPos, bias?: number) {
|
|
89
|
+
return view.someProp('createSelectionBetween', f => f(view, $anchor, $head))
|
|
90
|
+
|| TextSelection.between($anchor, $head, bias)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function createDropIndicatorView(view: EditorView, getTarget: GetTarget, options: DropIndicatorPluginOptions): PluginView {
|
|
94
|
+
let dom = view.dom
|
|
95
|
+
let hideId: ReturnType<typeof setTimeout> | undefined
|
|
96
|
+
let prevX: number | undefined
|
|
97
|
+
let prevY: number | undefined
|
|
98
|
+
let hasDragOverEvent: boolean = false
|
|
99
|
+
|
|
100
|
+
const scheduleHide = () => {
|
|
101
|
+
if (hideId) {
|
|
102
|
+
clearTimeout(hideId)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
hasDragOverEvent = false
|
|
106
|
+
hideId = setTimeout(() => {
|
|
107
|
+
if (hasDragOverEvent) return
|
|
108
|
+
options.onHide()
|
|
109
|
+
}, 30)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const handleDragOver = (event: DragEvent): void => {
|
|
113
|
+
hasDragOverEvent = true
|
|
114
|
+
|
|
115
|
+
const { clientX, clientY } = event
|
|
116
|
+
if (prevX === clientX && prevY === clientY) {
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
prevX = clientX
|
|
120
|
+
prevY = clientY
|
|
121
|
+
|
|
122
|
+
let target = getTarget([clientX, clientY], event)
|
|
123
|
+
|
|
124
|
+
if (!target) {
|
|
125
|
+
scheduleHide()
|
|
126
|
+
return
|
|
127
|
+
} else {
|
|
128
|
+
const [pos, [x1, y1, x2, y2]] = target
|
|
129
|
+
const line = { p1: { x: x1, y: y1 }, p2: { x: x2, y: y2 } }
|
|
130
|
+
options.onShow({ view, pos, line })
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
dom.addEventListener('dragover', handleDragOver)
|
|
135
|
+
dom.addEventListener('dragend', scheduleHide)
|
|
136
|
+
dom.addEventListener('drop', scheduleHide)
|
|
137
|
+
dom.addEventListener('dragleave', scheduleHide)
|
|
138
|
+
|
|
139
|
+
const destroy = () => {
|
|
140
|
+
dom.removeEventListener('dragover', handleDragOver)
|
|
141
|
+
dom.removeEventListener('dragend', scheduleHide)
|
|
142
|
+
dom.removeEventListener('drop', scheduleHide)
|
|
143
|
+
dom.removeEventListener('dragleave', scheduleHide)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return { destroy }
|
|
147
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { PlainExtension } from '@prosekit/core'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
defineDropIndicatorPayload,
|
|
5
|
+
type DropIndicatorPayload,
|
|
6
|
+
} from './drop-indicator-facet'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
export type DropIndicatorExtension = PlainExtension
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Defines an extension that controls the behavior of the drop indicator.
|
|
15
|
+
*
|
|
16
|
+
* This extension itself doesn't draw the drop indicator, but it provides the
|
|
17
|
+
* necessary callbacks to do so. You probably don't want to use this extension
|
|
18
|
+
* directly, but rather use the `<DropIndicator>` component.
|
|
19
|
+
*
|
|
20
|
+
* You can add this extension multiple times. If any extension has `onDrag`
|
|
21
|
+
* callback defined, and it returns `false`, then the drop point will be
|
|
22
|
+
* discarded.
|
|
23
|
+
*
|
|
24
|
+
* @public
|
|
25
|
+
*/
|
|
26
|
+
export function defineDropIndicator(
|
|
27
|
+
options?: DropIndicatorOptions,
|
|
28
|
+
): DropIndicatorExtension {
|
|
29
|
+
return defineDropIndicatorPayload(options ?? {})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Options for {@link defineDropIndicator}.
|
|
34
|
+
*
|
|
35
|
+
* @public
|
|
36
|
+
*/
|
|
37
|
+
export interface DropIndicatorOptions extends DropIndicatorPayload {}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { isHTMLElement } from '@ocavue/utils'
|
|
2
|
+
import { isNodeSelection } from '@prosekit/core'
|
|
3
|
+
import type { ProseMirrorNode } from '@prosekit/pm/model'
|
|
4
|
+
import type { EditorView } from '@prosekit/pm/view'
|
|
5
|
+
|
|
6
|
+
import type { DragEventHandler } from './types'
|
|
7
|
+
|
|
8
|
+
type Point = readonly [x: number, y: number]
|
|
9
|
+
|
|
10
|
+
type Line = readonly [x1: number, y1: number, x2: number, y2: number]
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
type DropTarget = readonly [pos: number, line: Line]
|
|
16
|
+
|
|
17
|
+
function getTargetsByView(view: EditorView): DropTarget[] {
|
|
18
|
+
type StackItem = [pos: number, node: ProseMirrorNode]
|
|
19
|
+
let stack: StackItem[] = [[-1, view.state.doc]]
|
|
20
|
+
let targets: DropTarget[] = []
|
|
21
|
+
|
|
22
|
+
while (stack.length > 0) {
|
|
23
|
+
const [pos, node] = stack.pop()!
|
|
24
|
+
if (pos >= 0) {
|
|
25
|
+
let dom = view.nodeDOM(pos)
|
|
26
|
+
if (dom && isHTMLElement(dom)) {
|
|
27
|
+
let rect = dom.getBoundingClientRect()
|
|
28
|
+
let { top, bottom, left: x1, right: x2 } = rect
|
|
29
|
+
targets.push(
|
|
30
|
+
[pos, [x1, top, x2, top]],
|
|
31
|
+
[pos + node.nodeSize, [x1, bottom, x2, bottom]],
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (node.isBlock && !node.isTextblock) {
|
|
36
|
+
let childPos = pos + 1
|
|
37
|
+
for (let child of node.children) {
|
|
38
|
+
stack.push([childPos, child])
|
|
39
|
+
childPos += child.nodeSize
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return targets
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @internal
|
|
49
|
+
*/
|
|
50
|
+
export type GetTarget = (point: Point, event: DragEvent) => DropTarget | undefined
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @internal
|
|
54
|
+
*/
|
|
55
|
+
export function buildGetTarget(
|
|
56
|
+
view: EditorView,
|
|
57
|
+
onDrag: DragEventHandler,
|
|
58
|
+
): GetTarget {
|
|
59
|
+
let prevTargets: DropTarget[] = []
|
|
60
|
+
let prevDoc: ProseMirrorNode | undefined
|
|
61
|
+
let prevRect: DOMRect | undefined
|
|
62
|
+
|
|
63
|
+
const getTargets = (): DropTarget[] => {
|
|
64
|
+
const rect = view.dom.getBoundingClientRect()
|
|
65
|
+
const doc = view.state.doc
|
|
66
|
+
|
|
67
|
+
if (
|
|
68
|
+
prevTargets && prevDoc && prevRect
|
|
69
|
+
&& rect.width === prevRect.width
|
|
70
|
+
&& rect.height === prevRect.height
|
|
71
|
+
&& rect.x === prevRect.x
|
|
72
|
+
&& rect.y === prevRect.y
|
|
73
|
+
&& prevDoc.eq(doc)
|
|
74
|
+
) {
|
|
75
|
+
return prevTargets
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
prevRect = rect
|
|
79
|
+
prevDoc = doc
|
|
80
|
+
prevTargets = getTargetsByView(view)
|
|
81
|
+
return prevTargets
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const getTargetImpl: GetTarget = (point, event) => {
|
|
85
|
+
if (!view.editable || view.isDestroyed) {
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const compare = (p1: DropTarget, p2: DropTarget): number => {
|
|
90
|
+
const [pos1, line1] = p1
|
|
91
|
+
const [pos2, line2] = p2
|
|
92
|
+
const p1Distance = pointLineDistance(point, line1)
|
|
93
|
+
const p2Distance = pointLineDistance(point, line2)
|
|
94
|
+
|
|
95
|
+
return (p1Distance - p2Distance) || (pos1 - pos2)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let targets = getTargets()
|
|
99
|
+
targets.sort(compare)
|
|
100
|
+
|
|
101
|
+
// Only consider the first few targets for performance reasons.
|
|
102
|
+
targets = targets.slice(0, 8)
|
|
103
|
+
|
|
104
|
+
// Find the closest valid target.
|
|
105
|
+
const target = targets.find(target => onDrag({ view, pos: target[0], event }) !== false)
|
|
106
|
+
|
|
107
|
+
// If the dragging node is already at the target position, we ignore this
|
|
108
|
+
// target. Notice that we don't pick the second better target here.
|
|
109
|
+
if (target && isDraggingToItself(view, target[0])) {
|
|
110
|
+
return undefined
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return target
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
let prevPoint: Point | undefined
|
|
117
|
+
let prevTarget: DropTarget | undefined
|
|
118
|
+
|
|
119
|
+
const getTargetCached: GetTarget = (point, event) => {
|
|
120
|
+
if (prevPoint && pointEqual(prevPoint, point)) {
|
|
121
|
+
return prevTarget
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
prevPoint = point
|
|
125
|
+
prevTarget = getTargetImpl(point, event)
|
|
126
|
+
return prevTarget
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return getTargetCached
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function pointEqual(a: Point, b: Point) {
|
|
133
|
+
return a[0] === b[0] && a[1] === b[1]
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function pointPointDistance(a: Point, b: Point) {
|
|
137
|
+
// Manhattan distance
|
|
138
|
+
return Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1])
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function pointLineDistance(point: Point, line: Line) {
|
|
142
|
+
// Notice that we are actually not calculating the distance between the point
|
|
143
|
+
// and the line. Instead, we are calculating the distance between the point
|
|
144
|
+
// and the two endpoints of the line.
|
|
145
|
+
return Math.min(
|
|
146
|
+
pointPointDistance(point, [line[0], line[1]]),
|
|
147
|
+
pointPointDistance(point, [line[2], line[3]]),
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Whether the dragging node is being dragged to the same position. For example,
|
|
153
|
+
* dragging a list node into a new position that is just below the list node, or
|
|
154
|
+
* dragging a nested quoteblock into itself.
|
|
155
|
+
*/
|
|
156
|
+
function isDraggingToItself(view: EditorView, pos: number) {
|
|
157
|
+
const dragging = view.dragging
|
|
158
|
+
if (!dragging) return
|
|
159
|
+
|
|
160
|
+
const { move } = dragging
|
|
161
|
+
if (!move) return
|
|
162
|
+
|
|
163
|
+
const selection = view.state.selection
|
|
164
|
+
if (!isNodeSelection(selection)) return
|
|
165
|
+
|
|
166
|
+
const { from, to } = selection
|
|
167
|
+
return from <= pos && pos <= to
|
|
168
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export {
|
|
2
|
+
defineDropIndicator,
|
|
3
|
+
type DropIndicatorExtension,
|
|
4
|
+
type DropIndicatorOptions,
|
|
5
|
+
} from './drop-indicator'
|
|
6
|
+
export type {
|
|
7
|
+
DragEventHandler,
|
|
8
|
+
DragEventHandlerOptions,
|
|
9
|
+
Line,
|
|
10
|
+
Point,
|
|
11
|
+
ShowHandler,
|
|
12
|
+
ShowHandlerOptions,
|
|
13
|
+
ViewDragging,
|
|
14
|
+
} from './types'
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { Slice } from '@prosekit/pm/model'
|
|
2
|
+
import type { NodeSelection } from '@prosekit/pm/state'
|
|
3
|
+
import type { EditorView } from '@prosekit/pm/view'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A function that will be called when the `dragover` event is fired. You can
|
|
7
|
+
* return `false` to disable the current drop point and thus hide the drop
|
|
8
|
+
* indicator.
|
|
9
|
+
*
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
12
|
+
export type DragEventHandler = (options: DragEventHandlerOptions) => boolean
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Options for {@link DragEventHandler}.
|
|
16
|
+
*
|
|
17
|
+
* @public
|
|
18
|
+
*/
|
|
19
|
+
export interface DragEventHandlerOptions {
|
|
20
|
+
/**
|
|
21
|
+
* The editor's view.
|
|
22
|
+
*/
|
|
23
|
+
view: EditorView
|
|
24
|
+
/**
|
|
25
|
+
* The drop position in current document.
|
|
26
|
+
*/
|
|
27
|
+
pos: number
|
|
28
|
+
/**
|
|
29
|
+
* The `dragover` event.
|
|
30
|
+
*/
|
|
31
|
+
event: DragEvent
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A function that will be called when the drop indicator should be shown.
|
|
36
|
+
*
|
|
37
|
+
* @public
|
|
38
|
+
*/
|
|
39
|
+
export type ShowHandler = (options: ShowHandlerOptions) => void
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Options for {@link ShowHandler}.
|
|
43
|
+
*
|
|
44
|
+
* @public
|
|
45
|
+
*/
|
|
46
|
+
export interface ShowHandlerOptions {
|
|
47
|
+
/**
|
|
48
|
+
* The editor's view.
|
|
49
|
+
*/
|
|
50
|
+
view: EditorView
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The ProseMirror position that the drop indicator should be shown at.
|
|
54
|
+
*/
|
|
55
|
+
pos: number
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The line that the drop indicator should be shown at.
|
|
59
|
+
*/
|
|
60
|
+
line: Line
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @internal
|
|
65
|
+
*/
|
|
66
|
+
export interface Point {
|
|
67
|
+
readonly x: number
|
|
68
|
+
readonly y: number
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @internal
|
|
73
|
+
*/
|
|
74
|
+
export interface Line {
|
|
75
|
+
readonly p1: Point
|
|
76
|
+
readonly p2: Point
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* An interface matching the internal ProseMirror implementation.
|
|
81
|
+
*
|
|
82
|
+
* See https://github.com/ProseMirror/prosemirror-view/blob/1.38.1/src/input.ts#L657
|
|
83
|
+
*
|
|
84
|
+
* @internal
|
|
85
|
+
*/
|
|
86
|
+
export interface ViewDragging {
|
|
87
|
+
readonly slice: Slice
|
|
88
|
+
readonly move: boolean
|
|
89
|
+
readonly node?: NodeSelection
|
|
90
|
+
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineFacet,
|
|
3
|
+
defineFacetPayload,
|
|
4
|
+
getNodeType,
|
|
5
|
+
isTextSelection,
|
|
6
|
+
maybeRun,
|
|
7
|
+
OBJECT_REPLACEMENT_CHARACTER,
|
|
8
|
+
pluginFacet,
|
|
9
|
+
type PlainExtension,
|
|
10
|
+
type PluginPayload,
|
|
11
|
+
} from '@prosekit/core'
|
|
12
|
+
import { keydownHandler } from '@prosekit/pm/keymap'
|
|
13
|
+
import type {
|
|
14
|
+
Attrs,
|
|
15
|
+
NodeType,
|
|
16
|
+
} from '@prosekit/pm/model'
|
|
17
|
+
import {
|
|
18
|
+
PluginKey,
|
|
19
|
+
ProseMirrorPlugin,
|
|
20
|
+
type Command,
|
|
21
|
+
type EditorState,
|
|
22
|
+
type Transaction,
|
|
23
|
+
} from '@prosekit/pm/state'
|
|
24
|
+
import type { EditorView } from '@prosekit/pm/view'
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @public
|
|
28
|
+
*
|
|
29
|
+
* Options for {@link EnterRuleHandler}.
|
|
30
|
+
*/
|
|
31
|
+
export interface EnterRuleHandlerOptions {
|
|
32
|
+
/**
|
|
33
|
+
* The current editor state.
|
|
34
|
+
*/
|
|
35
|
+
state: EditorState
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The start position of the matched text.
|
|
39
|
+
*/
|
|
40
|
+
from: number
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The end position of the matched text.
|
|
44
|
+
*/
|
|
45
|
+
to: number
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The matched result from the regular expression.
|
|
49
|
+
*/
|
|
50
|
+
match: RegExpExecArray
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @public
|
|
55
|
+
*/
|
|
56
|
+
export type EnterRuleHandler = (options: EnterRuleHandlerOptions) => Transaction | null
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Options for {@link defineEnterRule}.
|
|
60
|
+
*
|
|
61
|
+
* @public
|
|
62
|
+
*/
|
|
63
|
+
export type EnterRuleOptions = {
|
|
64
|
+
/**
|
|
65
|
+
* The regular expression to match against. It should end with `$`.
|
|
66
|
+
*/
|
|
67
|
+
regex: RegExp
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* A function to be called when an enter rule is triggered.
|
|
71
|
+
*/
|
|
72
|
+
handler: EnterRuleHandler
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Whether to stop further handlers from being called if this rule is triggered.
|
|
76
|
+
*
|
|
77
|
+
* @default false
|
|
78
|
+
*/
|
|
79
|
+
stop?: boolean
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Options for {@link defineTextBlockEnterRule}.
|
|
84
|
+
*
|
|
85
|
+
* @public
|
|
86
|
+
*/
|
|
87
|
+
export interface TextBlockEnterRuleOptions {
|
|
88
|
+
/**
|
|
89
|
+
* The regular expression to match against. It should end with `$`.
|
|
90
|
+
*/
|
|
91
|
+
regex: RegExp
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The node type to replace the matched text with.
|
|
95
|
+
*/
|
|
96
|
+
type: string | NodeType
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Attributes to set on the node. If a function is provided, it will be called
|
|
100
|
+
* with the matched result from the regular expression.
|
|
101
|
+
*/
|
|
102
|
+
attrs?: Attrs | null | ((match: RegExpMatchArray) => Attrs | null)
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Whether to stop further handlers from being called if this rule is triggered.
|
|
106
|
+
*
|
|
107
|
+
* @default true
|
|
108
|
+
*/
|
|
109
|
+
stop?: boolean
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Defines an enter rule. An enter rule applies when the text directly in front of
|
|
114
|
+
* the cursor matches `regex` and user presses Enter. The `regex` should end
|
|
115
|
+
* with `$`.
|
|
116
|
+
*
|
|
117
|
+
* @param options
|
|
118
|
+
*
|
|
119
|
+
* @public
|
|
120
|
+
*/
|
|
121
|
+
export function defineEnterRule({
|
|
122
|
+
regex,
|
|
123
|
+
handler,
|
|
124
|
+
stop = false,
|
|
125
|
+
}: EnterRuleOptions): PlainExtension {
|
|
126
|
+
const rule: EnterRule = new EnterRule(regex, handler, stop)
|
|
127
|
+
return defineFacetPayload(enterRule, [rule]) as PlainExtension
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Defines an enter rule that replaces the matched text with a block node.
|
|
132
|
+
*
|
|
133
|
+
* See also {@link defineEnterRule}.
|
|
134
|
+
*
|
|
135
|
+
* @param options
|
|
136
|
+
*
|
|
137
|
+
* @public
|
|
138
|
+
*/
|
|
139
|
+
export function defineTextBlockEnterRule({
|
|
140
|
+
regex,
|
|
141
|
+
type,
|
|
142
|
+
attrs,
|
|
143
|
+
stop = true,
|
|
144
|
+
}: TextBlockEnterRuleOptions): PlainExtension {
|
|
145
|
+
return defineEnterRule({
|
|
146
|
+
regex,
|
|
147
|
+
handler: ({ state, from, to, match }) => {
|
|
148
|
+
const nodeType = getNodeType(state.schema, type)
|
|
149
|
+
const $start = state.doc.resolve(from)
|
|
150
|
+
|
|
151
|
+
if (
|
|
152
|
+
!$start
|
|
153
|
+
.node(-1)
|
|
154
|
+
.canReplaceWith($start.index(-1), $start.indexAfter(-1), nodeType)
|
|
155
|
+
) {
|
|
156
|
+
return null
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const nodeAttrs = maybeRun(attrs, match)
|
|
160
|
+
return state.tr
|
|
161
|
+
.delete(from, to)
|
|
162
|
+
.setBlockType(from, from, nodeType, nodeAttrs)
|
|
163
|
+
},
|
|
164
|
+
stop,
|
|
165
|
+
})
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* @internal
|
|
170
|
+
*/
|
|
171
|
+
class EnterRule {
|
|
172
|
+
constructor(
|
|
173
|
+
readonly regex: RegExp,
|
|
174
|
+
readonly handler: EnterRuleHandler,
|
|
175
|
+
readonly stop: boolean,
|
|
176
|
+
) {}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const enterRule = defineFacet<EnterRule, PluginPayload>({
|
|
180
|
+
reduce: () => {
|
|
181
|
+
let rules: EnterRule[] = []
|
|
182
|
+
|
|
183
|
+
const command: Command = (state, dispatch, view) => {
|
|
184
|
+
if (!view) return false
|
|
185
|
+
return execRules(view, rules, dispatch)
|
|
186
|
+
}
|
|
187
|
+
const handler = keydownHandler({ Enter: command })
|
|
188
|
+
const plugin = new ProseMirrorPlugin({
|
|
189
|
+
key: new PluginKey('prosekit-enter-rule'),
|
|
190
|
+
props: { handleKeyDown: handler },
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
return function reducer(inputs) {
|
|
194
|
+
rules = inputs
|
|
195
|
+
return plugin
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
parent: pluginFacet,
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
function execRules(
|
|
203
|
+
view: EditorView,
|
|
204
|
+
rules: readonly EnterRule[],
|
|
205
|
+
dispatch?: (tr: Transaction) => void,
|
|
206
|
+
): boolean {
|
|
207
|
+
if (view.composing) return false
|
|
208
|
+
const state = view.state
|
|
209
|
+
const selection = state.selection
|
|
210
|
+
if (!isTextSelection(selection)) return false
|
|
211
|
+
const $cursor = selection.$cursor
|
|
212
|
+
if (!$cursor || $cursor.parent.type.spec.code) return false
|
|
213
|
+
|
|
214
|
+
const textBefore = $cursor.parent.textBetween(
|
|
215
|
+
Math.max(0, $cursor.parentOffset - MAX_MATCH),
|
|
216
|
+
$cursor.parentOffset,
|
|
217
|
+
null,
|
|
218
|
+
OBJECT_REPLACEMENT_CHARACTER,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
for (const rule of rules) {
|
|
222
|
+
rule.regex.lastIndex = 0
|
|
223
|
+
const match = rule.regex.exec(textBefore)
|
|
224
|
+
const tr = match
|
|
225
|
+
&& rule.handler({
|
|
226
|
+
state,
|
|
227
|
+
from: $cursor.pos - match[0].length,
|
|
228
|
+
to: $cursor.pos,
|
|
229
|
+
match,
|
|
230
|
+
})
|
|
231
|
+
if (!tr) continue
|
|
232
|
+
dispatch?.(tr)
|
|
233
|
+
|
|
234
|
+
if (rule.stop) {
|
|
235
|
+
return true
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return false
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const MAX_MATCH = 200
|