diff --git a/docs/src/demos/Experiments/Colors/extension/Colors.ts b/docs/src/demos/Experiments/Colors/extension/Colors.ts index 4a773f48..2c94a8e8 100644 --- a/docs/src/demos/Experiments/Colors/extension/Colors.ts +++ b/docs/src/demos/Experiments/Colors/extension/Colors.ts @@ -17,7 +17,6 @@ function detectColors(doc) { let matches // eslint-disable-next-line - let regex = hexColors while (matches = hexColors.exec(node.text)) { results.push({ color: matches[0], @@ -26,6 +25,7 @@ function detectColors(doc) { }) } + // eslint-disable-next-line while (matches = rgbaColors.exec(node.text)) { results.push({ color: matches[0], diff --git a/docs/src/docPages/api/commands.md b/docs/src/docPages/api/commands.md index 4762e50a..7b9e68db 100644 --- a/docs/src/docPages/api/commands.md +++ b/docs/src/docPages/api/commands.md @@ -147,7 +147,8 @@ Have a look at all of the core commands listed below. They should give you a goo | .lift() | Removes an existing wrap. | | .liftEmptyBlock() | Lift block if empty. | | .newlineInCode() | Add a newline character in code. | -| .replace() | Replaces text with a node within a range. | +| .replace() | Replaces text with a node. | +| .replaceRange() | Replaces text with a node within a range. | | .resetNodeAttributes() | Resets all node attributes to the default value. | | .selectParentNode() | Select the parent node. | | .setMark() | Add a mark with new attributes. | diff --git a/packages/core/src/commands/replace.ts b/packages/core/src/commands/replace.ts index ae29981a..1535b09e 100644 --- a/packages/core/src/commands/replace.ts +++ b/packages/core/src/commands/replace.ts @@ -1,24 +1,12 @@ import { NodeType } from 'prosemirror-model' -import getNodeType from '../helpers/getNodeType' -import { Command, Range, AnyObject } from '../types' +import { Command, AnyObject } from '../types' /** - * Replaces text with a node within a range. + * Replaces text with a node. */ -export const replace = (range: Range | null = null, typeOrName: string | NodeType, attrs: AnyObject = {}): Command => ({ tr, state, dispatch }) => { - const type = getNodeType(typeOrName, state.schema) - const { $from, $to } = state.selection - const index = $from.index() - const from = range ? range.from : $from.pos - const to = range ? range.to : $to.pos +export const replace = (typeOrName: string | NodeType, attributes: AnyObject = {}): Command => ({ state, commands }) => { + const { from, to } = state.selection + const range = { from, to } - if (!$from.parent.canReplaceWith(index, index, type)) { - return false - } - - if (dispatch) { - tr.replaceWith(from, to, type.create(attrs)) - } - - return true + return commands.replaceRange(range, typeOrName, attributes) } diff --git a/packages/core/src/commands/replaceRange.ts b/packages/core/src/commands/replaceRange.ts new file mode 100644 index 00000000..bb87a774 --- /dev/null +++ b/packages/core/src/commands/replaceRange.ts @@ -0,0 +1,23 @@ +import { NodeType } from 'prosemirror-model' +import getNodeType from '../helpers/getNodeType' +import { Command, Range, AnyObject } from '../types' + +/** + * Replaces text with a node within a range. + */ +export const replaceRange = (range: Range, typeOrName: string | NodeType, attributes: AnyObject = {}): Command => ({ tr, state, dispatch }) => { + const type = getNodeType(typeOrName, state.schema) + const { from, to } = range + const $from = tr.doc.resolve(from) + const index = $from.index() + + if (!$from.parent.canReplaceWith(index, index, type)) { + return false + } + + if (dispatch) { + tr.replaceWith(from, to, type.create(attributes)) + } + + return true +} diff --git a/packages/core/src/extensions/commands.ts b/packages/core/src/extensions/commands.ts index c52f3da1..117b5708 100644 --- a/packages/core/src/extensions/commands.ts +++ b/packages/core/src/extensions/commands.ts @@ -18,6 +18,7 @@ import * as liftEmptyBlock from '../commands/liftEmptyBlock' import * as liftListItem from '../commands/liftListItem' import * as newlineInCode from '../commands/newlineInCode' import * as replace from '../commands/replace' +import * as replaceRange from '../commands/replaceRange' import * as resetNodeAttributes from '../commands/resetNodeAttributes' import * as scrollIntoView from '../commands/scrollIntoView' import * as selectAll from '../commands/selectAll' @@ -65,6 +66,7 @@ export const Commands = Extension.create({ ...liftListItem, ...newlineInCode, ...replace, + ...replaceRange, ...resetNodeAttributes, ...scrollIntoView, ...selectAll, diff --git a/packages/extension-mention/src/mention.ts b/packages/extension-mention/src/mention.ts index e116a789..b5db04f4 100644 --- a/packages/extension-mention/src/mention.ts +++ b/packages/extension-mention/src/mention.ts @@ -19,7 +19,7 @@ export const Mention = Node.create({ editor .chain() .focus() - .replace(range, 'mention', attributes) + .replaceRange(range, 'mention', attributes) .insertText(' ') .run() }, @@ -72,6 +72,31 @@ export const Mention = Node.create({ return `@${node.attrs.id}` }, + addKeyboardShortcuts() { + return { + Backspace: () => this.editor.commands.command(({ tr, state }) => { + let isMention = false + const { selection } = state + const { empty, anchor } = selection + + if (!empty) { + return false + } + + state.doc.nodesBetween(anchor - 1, anchor, (node, pos) => { + if (node.type.name === 'mention') { + isMention = true + tr.insertText(this.options.suggestion.char || '', pos, pos + node.nodeSize) + + return false + } + }) + + return isMention + }), + } + }, + addProseMirrorPlugins() { return [ Suggestion({ diff --git a/yarn.lock b/yarn.lock index b022a841..45a501f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1143,10 +1143,10 @@ debug "^3.1.0" lodash.once "^4.1.1" -"@eslint/eslintrc@^0.2.2": - version "0.2.2" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.2.2.tgz#d01fc791e2fc33e88a29d6f3dc7e93d0cd784b76" - integrity sha512-EfB5OHNYp1F4px/LI/FEnGylop7nOqkQ1LRzCM0KccA2U8tvV8w01KBv37LbO7nW4H+YhKyo2LcJhRwjjV17QQ== +"@eslint/eslintrc@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.3.0.tgz#d736d6963d7003b6514e6324bec9c602ac340318" + integrity sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg== dependencies: ajv "^6.12.4" debug "^4.1.1" @@ -1155,7 +1155,7 @@ ignore "^4.0.6" import-fresh "^3.2.1" js-yaml "^3.13.1" - lodash "^4.17.19" + lodash "^4.17.20" minimatch "^3.0.4" strip-json-comments "^3.1.1" @@ -2258,9 +2258,9 @@ resolve "^1.17.0" "@rollup/plugin-node-resolve@^11.0.1": - version "11.0.1" - resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.0.1.tgz#d3765eec4bccf960801439a999382aed2dca959b" - integrity sha512-ltlsj/4Bhwwhb+Nb5xCz/6vieuEj2/BAkkqVIKmZwC7pIdl8srmgmglE4S0jFlZa32K4qvdQ6NHdmpRKD/LwoQ== + version "11.1.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.1.0.tgz#fa0f888297b3aebcd6534e8aba4e6fe01997649a" + integrity sha512-ouBBppRdWJKCllDXGzJ7ZIkYbaq+5TmyP0smt1vdJCFfoZhLi31vhpmjLhyo8lreHf4RoeSNllaWrvSqHpHRog== dependencies: "@rollup/pluginutils" "^3.1.0" "@types/resolve" "1.17.1" @@ -2529,60 +2529,61 @@ "@types/vfile-message" "*" "@typescript-eslint/eslint-plugin@^4.12.0": - version "4.12.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.12.0.tgz#00d1b23b40b58031e6d7c04a5bc6c1a30a2e834a" - integrity sha512-wHKj6q8s70sO5i39H2g1gtpCXCvjVszzj6FFygneNFyIAxRvNSVz9GML7XpqrB9t7hNutXw+MHnLN/Ih6uyB8Q== + version "4.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.13.0.tgz#5f580ea520fa46442deb82c038460c3dd3524bb6" + integrity sha512-ygqDUm+BUPvrr0jrXqoteMqmIaZ/bixYOc3A4BRwzEPTZPi6E+n44rzNZWaB0YvtukgP+aoj0i/fyx7FkM2p1w== dependencies: - "@typescript-eslint/experimental-utils" "4.12.0" - "@typescript-eslint/scope-manager" "4.12.0" + "@typescript-eslint/experimental-utils" "4.13.0" + "@typescript-eslint/scope-manager" "4.13.0" debug "^4.1.1" functional-red-black-tree "^1.0.1" + lodash "^4.17.15" regexpp "^3.0.0" semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@4.12.0": - version "4.12.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.12.0.tgz#372838e76db76c9a56959217b768a19f7129546b" - integrity sha512-MpXZXUAvHt99c9ScXijx7i061o5HEjXltO+sbYfZAAHxv3XankQkPaNi5myy0Yh0Tyea3Hdq1pi7Vsh0GJb0fA== +"@typescript-eslint/experimental-utils@4.13.0": + version "4.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.13.0.tgz#9dc9ab375d65603b43d938a0786190a0c72be44e" + integrity sha512-/ZsuWmqagOzNkx30VWYV3MNB/Re/CGv/7EzlqZo5RegBN8tMuPaBgNK6vPBCQA8tcYrbsrTdbx3ixMRRKEEGVw== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/scope-manager" "4.12.0" - "@typescript-eslint/types" "4.12.0" - "@typescript-eslint/typescript-estree" "4.12.0" + "@typescript-eslint/scope-manager" "4.13.0" + "@typescript-eslint/types" "4.13.0" + "@typescript-eslint/typescript-estree" "4.13.0" eslint-scope "^5.0.0" eslint-utils "^2.0.0" "@typescript-eslint/parser@^4.12.0": - version "4.12.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.12.0.tgz#e1cf30436e4f916c31fcc962158917bd9e9d460a" - integrity sha512-9XxVADAo9vlfjfoxnjboBTxYOiNY93/QuvcPgsiKvHxW6tOZx1W4TvkIQ2jB3k5M0pbFP5FlXihLK49TjZXhuQ== + version "4.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.13.0.tgz#c413d640ea66120cfcc37f891e8cb3fd1c9d247d" + integrity sha512-KO0J5SRF08pMXzq9+abyHnaGQgUJZ3Z3ax+pmqz9vl81JxmTTOUfQmq7/4awVfq09b6C4owNlOgOwp61pYRBSg== dependencies: - "@typescript-eslint/scope-manager" "4.12.0" - "@typescript-eslint/types" "4.12.0" - "@typescript-eslint/typescript-estree" "4.12.0" + "@typescript-eslint/scope-manager" "4.13.0" + "@typescript-eslint/types" "4.13.0" + "@typescript-eslint/typescript-estree" "4.13.0" debug "^4.1.1" -"@typescript-eslint/scope-manager@4.12.0": - version "4.12.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.12.0.tgz#beeb8beca895a07b10c593185a5612f1085ef279" - integrity sha512-QVf9oCSVLte/8jvOsxmgBdOaoe2J0wtEmBr13Yz0rkBNkl5D8bfnf6G4Vhox9qqMIoG7QQoVwd2eG9DM/ge4Qg== +"@typescript-eslint/scope-manager@4.13.0": + version "4.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.13.0.tgz#5b45912a9aa26b29603d8fa28f5e09088b947141" + integrity sha512-UpK7YLG2JlTp/9G4CHe7GxOwd93RBf3aHO5L+pfjIrhtBvZjHKbMhBXTIQNkbz7HZ9XOe++yKrXutYm5KmjWgQ== dependencies: - "@typescript-eslint/types" "4.12.0" - "@typescript-eslint/visitor-keys" "4.12.0" + "@typescript-eslint/types" "4.13.0" + "@typescript-eslint/visitor-keys" "4.13.0" -"@typescript-eslint/types@4.12.0": - version "4.12.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.12.0.tgz#fb891fe7ccc9ea8b2bbd2780e36da45d0dc055e5" - integrity sha512-N2RhGeheVLGtyy+CxRmxdsniB7sMSCfsnbh8K/+RUIXYYq3Ub5+sukRCjVE80QerrUBvuEvs4fDhz5AW/pcL6g== +"@typescript-eslint/types@4.13.0": + version "4.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.13.0.tgz#6a7c6015a59a08fbd70daa8c83dfff86250502f8" + integrity sha512-/+aPaq163oX+ObOG00M0t9tKkOgdv9lq0IQv/y4SqGkAXmhFmCfgsELV7kOCTb2vVU5VOmVwXBXJTDr353C1rQ== -"@typescript-eslint/typescript-estree@4.12.0": - version "4.12.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.12.0.tgz#3963418c850f564bdab3882ae23795d115d6d32e" - integrity sha512-gZkFcmmp/CnzqD2RKMich2/FjBTsYopjiwJCroxqHZIY11IIoN0l5lKqcgoAPKHt33H2mAkSfvzj8i44Jm7F4w== +"@typescript-eslint/typescript-estree@4.13.0": + version "4.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.13.0.tgz#cf6e2207c7d760f5dfd8d18051428fadfc37b45e" + integrity sha512-9A0/DFZZLlGXn5XA349dWQFwPZxcyYyCFX5X88nWs2uachRDwGeyPz46oTsm9ZJE66EALvEns1lvBwa4d9QxMg== dependencies: - "@typescript-eslint/types" "4.12.0" - "@typescript-eslint/visitor-keys" "4.12.0" + "@typescript-eslint/types" "4.13.0" + "@typescript-eslint/visitor-keys" "4.13.0" debug "^4.1.1" globby "^11.0.1" is-glob "^4.0.1" @@ -2590,12 +2591,12 @@ semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/visitor-keys@4.12.0": - version "4.12.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.12.0.tgz#a470a79be6958075fa91c725371a83baf428a67a" - integrity sha512-hVpsLARbDh4B9TKYz5cLbcdMIOAoBYgFPCSP9FFS/liSF+b33gVNq8JHY3QGhHNVz85hObvL7BEYLlgx553WCw== +"@typescript-eslint/visitor-keys@4.13.0": + version "4.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.13.0.tgz#9acb1772d3b3183182b6540d3734143dce9476fe" + integrity sha512-6RoxWK05PAibukE7jElqAtNMq+RWZyqJ6Q/GdIxaiUj2Ept8jh8+FUVlbq9WxMYxkmEOPvCE5cRSyupMpwW31g== dependencies: - "@typescript-eslint/types" "4.12.0" + "@typescript-eslint/types" "4.13.0" eslint-visitor-keys "^2.0.0" "@vue/babel-helper-vue-jsx-merge-props@^1.2.1": @@ -6265,12 +6266,12 @@ eslint-visitor-keys@^2.0.0: integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== eslint@^7.15.0: - version "7.17.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.17.0.tgz#4ccda5bf12572ad3bf760e6f195886f50569adb0" - integrity sha512-zJk08MiBgwuGoxes5sSQhOtibZ75pz0J35XTRlZOk9xMffhpA9BTbQZxoXZzOl5zMbleShbGwtw+1kGferfFwQ== + version "7.18.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.18.0.tgz#7fdcd2f3715a41fe6295a16234bd69aed2c75e67" + integrity sha512-fbgTiE8BfUJZuBeq2Yi7J3RB3WGUQ9PNuNbmgi6jt9Iv8qrkxfy19Ds3OpL1Pm7zg3BtTVhvcUZbIRQ0wmSjAQ== dependencies: "@babel/code-frame" "^7.0.0" - "@eslint/eslintrc" "^0.2.2" + "@eslint/eslintrc" "^0.3.0" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" @@ -6294,7 +6295,7 @@ eslint@^7.15.0: js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" - lodash "^4.17.19" + lodash "^4.17.20" minimatch "^3.0.4" natural-compare "^1.4.0" optionator "^0.9.1" @@ -11717,9 +11718,9 @@ property-information@^5.0.0, property-information@^5.2.0: xtend "^4.0.0" prosemirror-commands@^1.1.3: - version "1.1.4" - resolved "https://registry.yarnpkg.com/prosemirror-commands/-/prosemirror-commands-1.1.4.tgz#991563e67623acab4f8c510fad1570f8b4693780" - integrity sha512-kj4Qi+8h3EpJtZuuEDwZ9h2/QNGWDsIX/CzjmClxi9GhxWyBUMVUvIFk0mgdqHyX20lLeGmOpc0TLA5aPzgpWg== + version "1.1.5" + resolved "https://registry.yarnpkg.com/prosemirror-commands/-/prosemirror-commands-1.1.5.tgz#3f07a5b13e424ad8728168b6b45e1b17e47c2b81" + integrity sha512-4CKAnDxLTtUHpjRZZVEF/LLMUYh7NbS3Ze3mP5UEAgar4WRWQYg3Js01wnp/GMqaZueNHhsp9UVvOrAK+7DWbQ== dependencies: prosemirror-model "^1.0.0" prosemirror-state "^1.0.0" @@ -11816,9 +11817,9 @@ prosemirror-utils@^1.0.0-0: integrity sha512-11hTMG4Qwqlux6Vwp/4m16mLDg6IwWb0/odsWXGtWvvRJo61SfG0RGYlA8H72vExmbnWpiXa7PNenZ6t12Rkqw== prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.13.3, prosemirror-view@^1.16.3: - version "1.16.5" - resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.16.5.tgz#1a4646832e16c1cf116b54b9becf4b0663821125" - integrity sha512-cFEjzhqQZIRDALEgQt8CNn+Qb+BUOvNxxaljaWoCbAYlsWGMiNNQG06I1MwbRNDcwnZKeFmOGpLEB4eorYYGig== + version "1.17.2" + resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.17.2.tgz#666c865ae79e129a8933112bdcdf218a42c5a3b5" + integrity sha512-8jHmdl1q/Mvjfv185I5FldBitkkVpNOIK0Z/jIuan4cZIqXRpKu7DxxeLrTouJDzgrf1kWvfG/szEb6Bg9/4dA== dependencies: prosemirror-model "^1.1.0" prosemirror-state "^1.0.0" @@ -12796,9 +12797,9 @@ rollup-pluginutils@^2.8.2: estree-walker "^0.6.1" rollup@^2.36.0: - version "2.36.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.36.0.tgz#af2cdea36f70fa3de586840c2604882dfb4d0aac" - integrity sha512-L38QyQK77bkJy9nPyeydnHFK6xMofqumh4scTV2d4RG4EFq6pGdxnn67dVHFUDJ9J0PSEQx8zn1FiVS5TydsKg== + version "2.37.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.37.0.tgz#5baf631dc224a56d7eb88dd11b20aa28c1e8dc6f" + integrity sha512-cbxuxkMGCQV+TnVh+yZSUerbVb5i8soRydbzHYoMNojgt7MMi+jDLLs24U9HHCssKkwkXmsj+LXcOZMldTbz2w== optionalDependencies: fsevents "~2.1.2"