@tiptap/extensions 3.0.0-next.3

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.
Files changed (44) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +18 -0
  3. package/dist/focus/index.cjs +95 -0
  4. package/dist/focus/index.cjs.map +1 -0
  5. package/dist/focus/index.d.cts +28 -0
  6. package/dist/focus/index.d.ts +28 -0
  7. package/dist/focus/index.js +68 -0
  8. package/dist/focus/index.js.map +1 -0
  9. package/dist/gap-cursor/index.cjs +51 -0
  10. package/dist/gap-cursor/index.cjs.map +1 -0
  11. package/dist/gap-cursor/index.d.cts +25 -0
  12. package/dist/gap-cursor/index.d.ts +25 -0
  13. package/dist/gap-cursor/index.js +24 -0
  14. package/dist/gap-cursor/index.js.map +1 -0
  15. package/dist/index.cjs +222 -0
  16. package/dist/index.cjs.map +1 -0
  17. package/dist/index.d.cts +117 -0
  18. package/dist/index.d.ts +117 -0
  19. package/dist/index.js +191 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/selection/index.cjs +63 -0
  22. package/dist/selection/index.cjs.map +1 -0
  23. package/dist/selection/index.d.cts +17 -0
  24. package/dist/selection/index.d.ts +17 -0
  25. package/dist/selection/index.js +36 -0
  26. package/dist/selection/index.js.map +1 -0
  27. package/dist/trailing-node/index.cjs +78 -0
  28. package/dist/trailing-node/index.cjs.map +1 -0
  29. package/dist/trailing-node/index.d.cts +28 -0
  30. package/dist/trailing-node/index.d.ts +28 -0
  31. package/dist/trailing-node/index.js +51 -0
  32. package/dist/trailing-node/index.js.map +1 -0
  33. package/package.json +93 -0
  34. package/src/drop-cursor/drop-cursor.ts +47 -0
  35. package/src/drop-cursor/index.ts +1 -0
  36. package/src/focus/focus.ts +110 -0
  37. package/src/focus/index.ts +1 -0
  38. package/src/gap-cursor/gap-cursor.ts +46 -0
  39. package/src/gap-cursor/index.ts +1 -0
  40. package/src/index.ts +5 -0
  41. package/src/selection/index.ts +1 -0
  42. package/src/selection/selection.ts +51 -0
  43. package/src/trailing-node/index.ts +1 -0
  44. package/src/trailing-node/trailing-node.ts +84 -0
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/trailing-node/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ TrailingNode: () => TrailingNode
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+
27
+ // src/trailing-node/trailing-node.ts
28
+ var import_core = require("@tiptap/core");
29
+ var import_state = require("@tiptap/pm/state");
30
+ function nodeEqualsType({ types, node }) {
31
+ return node && Array.isArray(types) && types.includes(node.type) || (node == null ? void 0 : node.type) === types;
32
+ }
33
+ var TrailingNode = import_core.Extension.create({
34
+ name: "trailingNode",
35
+ addOptions() {
36
+ return {
37
+ node: "paragraph",
38
+ notAfter: []
39
+ };
40
+ },
41
+ addProseMirrorPlugins() {
42
+ const plugin = new import_state.PluginKey(this.name);
43
+ const disabledNodes = Object.entries(this.editor.schema.nodes).map(([, value]) => value).filter((node) => (this.options.notAfter || []).concat(this.options.node).includes(node.name));
44
+ return [
45
+ new import_state.Plugin({
46
+ key: plugin,
47
+ appendTransaction: (_, __, state) => {
48
+ const { doc, tr, schema } = state;
49
+ const shouldInsertNodeAtEnd = plugin.getState(state);
50
+ const endPosition = doc.content.size;
51
+ const type = schema.nodes[this.options.node];
52
+ if (!shouldInsertNodeAtEnd) {
53
+ return;
54
+ }
55
+ return tr.insert(endPosition, type.create());
56
+ },
57
+ state: {
58
+ init: (_, state) => {
59
+ const lastNode = state.tr.doc.lastChild;
60
+ return !nodeEqualsType({ node: lastNode, types: disabledNodes });
61
+ },
62
+ apply: (tr, value) => {
63
+ if (!tr.docChanged) {
64
+ return value;
65
+ }
66
+ const lastNode = tr.doc.lastChild;
67
+ return !nodeEqualsType({ node: lastNode, types: disabledNodes });
68
+ }
69
+ }
70
+ })
71
+ ];
72
+ }
73
+ });
74
+ // Annotate the CommonJS export names for ESM import in node:
75
+ 0 && (module.exports = {
76
+ TrailingNode
77
+ });
78
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/trailing-node/index.ts","../../src/trailing-node/trailing-node.ts"],"sourcesContent":["export * from './trailing-node.js'\n","import { Extension } from '@tiptap/core'\nimport { Node, NodeType } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\n\nfunction nodeEqualsType({ types, node }: { types: NodeType | NodeType[]; node: Node | null | undefined }) {\n return (node && Array.isArray(types) && types.includes(node.type)) || node?.type === types\n}\n\n/**\n * Extension based on:\n * - https://github.com/ueberdosis/tiptap/blob/v1/packages/tiptap-extensions/src/extensions/TrailingNode.js\n * - https://github.com/remirror/remirror/blob/e0f1bec4a1e8073ce8f5500d62193e52321155b9/packages/prosemirror-trailing-node/src/trailing-node-plugin.ts\n */\n\nexport interface TrailingNodeOptions {\n /**\n * The node type that should be inserted at the end of the document.\n * @note the node will always be added to the `notAfter` lists to\n * prevent an infinite loop.\n * @default 'paragraph'\n */\n node: string\n /**\n * The node types after which the trailing node should not be inserted.\n * @default ['paragraph']\n */\n notAfter?: string | string[]\n}\n\n/**\n * This extension allows you to add an extra node at the end of the document.\n * @see https://www.tiptap.dev/api/extensions/trailing-node\n */\nexport const TrailingNode = Extension.create<TrailingNodeOptions>({\n name: 'trailingNode',\n\n addOptions() {\n return {\n node: 'paragraph',\n notAfter: [],\n }\n },\n\n addProseMirrorPlugins() {\n const plugin = new PluginKey(this.name)\n const disabledNodes = Object.entries(this.editor.schema.nodes)\n .map(([, value]) => value)\n .filter(node => (this.options.notAfter || []).concat(this.options.node).includes(node.name))\n\n return [\n new Plugin({\n key: plugin,\n appendTransaction: (_, __, state) => {\n const { doc, tr, schema } = state\n const shouldInsertNodeAtEnd = plugin.getState(state)\n const endPosition = doc.content.size\n const type = schema.nodes[this.options.node]\n\n if (!shouldInsertNodeAtEnd) {\n return\n }\n\n return tr.insert(endPosition, type.create())\n },\n state: {\n init: (_, state) => {\n const lastNode = state.tr.doc.lastChild\n\n return !nodeEqualsType({ node: lastNode, types: disabledNodes })\n },\n apply: (tr, value) => {\n if (!tr.docChanged) {\n return value\n }\n\n const lastNode = tr.doc.lastChild\n\n return !nodeEqualsType({ node: lastNode, types: disabledNodes })\n },\n },\n }),\n ]\n },\n})\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kBAA0B;AAE1B,mBAAkC;AAElC,SAAS,eAAe,EAAE,OAAO,KAAK,GAAoE;AACxG,SAAQ,QAAQ,MAAM,QAAQ,KAAK,KAAK,MAAM,SAAS,KAAK,IAAI,MAAM,6BAAM,UAAS;AACvF;AA2BO,IAAM,eAAe,sBAAU,OAA4B;AAAA,EAChE,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU,CAAC;AAAA,IACb;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,UAAM,SAAS,IAAI,uBAAU,KAAK,IAAI;AACtC,UAAM,gBAAgB,OAAO,QAAQ,KAAK,OAAO,OAAO,KAAK,EAC1D,IAAI,CAAC,CAAC,EAAE,KAAK,MAAM,KAAK,EACxB,OAAO,WAAS,KAAK,QAAQ,YAAY,CAAC,GAAG,OAAO,KAAK,QAAQ,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;AAE7F,WAAO;AAAA,MACL,IAAI,oBAAO;AAAA,QACT,KAAK;AAAA,QACL,mBAAmB,CAAC,GAAG,IAAI,UAAU;AACnC,gBAAM,EAAE,KAAK,IAAI,OAAO,IAAI;AAC5B,gBAAM,wBAAwB,OAAO,SAAS,KAAK;AACnD,gBAAM,cAAc,IAAI,QAAQ;AAChC,gBAAM,OAAO,OAAO,MAAM,KAAK,QAAQ,IAAI;AAE3C,cAAI,CAAC,uBAAuB;AAC1B;AAAA,UACF;AAEA,iBAAO,GAAG,OAAO,aAAa,KAAK,OAAO,CAAC;AAAA,QAC7C;AAAA,QACA,OAAO;AAAA,UACL,MAAM,CAAC,GAAG,UAAU;AAClB,kBAAM,WAAW,MAAM,GAAG,IAAI;AAE9B,mBAAO,CAAC,eAAe,EAAE,MAAM,UAAU,OAAO,cAAc,CAAC;AAAA,UACjE;AAAA,UACA,OAAO,CAAC,IAAI,UAAU;AACpB,gBAAI,CAAC,GAAG,YAAY;AAClB,qBAAO;AAAA,YACT;AAEA,kBAAM,WAAW,GAAG,IAAI;AAExB,mBAAO,CAAC,eAAe,EAAE,MAAM,UAAU,OAAO,cAAc,CAAC;AAAA,UACjE;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;","names":[]}
@@ -0,0 +1,28 @@
1
+ import { Extension } from '@tiptap/core';
2
+
3
+ /**
4
+ * Extension based on:
5
+ * - https://github.com/ueberdosis/tiptap/blob/v1/packages/tiptap-extensions/src/extensions/TrailingNode.js
6
+ * - https://github.com/remirror/remirror/blob/e0f1bec4a1e8073ce8f5500d62193e52321155b9/packages/prosemirror-trailing-node/src/trailing-node-plugin.ts
7
+ */
8
+ interface TrailingNodeOptions {
9
+ /**
10
+ * The node type that should be inserted at the end of the document.
11
+ * @note the node will always be added to the `notAfter` lists to
12
+ * prevent an infinite loop.
13
+ * @default 'paragraph'
14
+ */
15
+ node: string;
16
+ /**
17
+ * The node types after which the trailing node should not be inserted.
18
+ * @default ['paragraph']
19
+ */
20
+ notAfter?: string | string[];
21
+ }
22
+ /**
23
+ * This extension allows you to add an extra node at the end of the document.
24
+ * @see https://www.tiptap.dev/api/extensions/trailing-node
25
+ */
26
+ declare const TrailingNode: Extension<TrailingNodeOptions, any>;
27
+
28
+ export { TrailingNode, type TrailingNodeOptions };
@@ -0,0 +1,28 @@
1
+ import { Extension } from '@tiptap/core';
2
+
3
+ /**
4
+ * Extension based on:
5
+ * - https://github.com/ueberdosis/tiptap/blob/v1/packages/tiptap-extensions/src/extensions/TrailingNode.js
6
+ * - https://github.com/remirror/remirror/blob/e0f1bec4a1e8073ce8f5500d62193e52321155b9/packages/prosemirror-trailing-node/src/trailing-node-plugin.ts
7
+ */
8
+ interface TrailingNodeOptions {
9
+ /**
10
+ * The node type that should be inserted at the end of the document.
11
+ * @note the node will always be added to the `notAfter` lists to
12
+ * prevent an infinite loop.
13
+ * @default 'paragraph'
14
+ */
15
+ node: string;
16
+ /**
17
+ * The node types after which the trailing node should not be inserted.
18
+ * @default ['paragraph']
19
+ */
20
+ notAfter?: string | string[];
21
+ }
22
+ /**
23
+ * This extension allows you to add an extra node at the end of the document.
24
+ * @see https://www.tiptap.dev/api/extensions/trailing-node
25
+ */
26
+ declare const TrailingNode: Extension<TrailingNodeOptions, any>;
27
+
28
+ export { TrailingNode, type TrailingNodeOptions };
@@ -0,0 +1,51 @@
1
+ // src/trailing-node/trailing-node.ts
2
+ import { Extension } from "@tiptap/core";
3
+ import { Plugin, PluginKey } from "@tiptap/pm/state";
4
+ function nodeEqualsType({ types, node }) {
5
+ return node && Array.isArray(types) && types.includes(node.type) || (node == null ? void 0 : node.type) === types;
6
+ }
7
+ var TrailingNode = Extension.create({
8
+ name: "trailingNode",
9
+ addOptions() {
10
+ return {
11
+ node: "paragraph",
12
+ notAfter: []
13
+ };
14
+ },
15
+ addProseMirrorPlugins() {
16
+ const plugin = new PluginKey(this.name);
17
+ const disabledNodes = Object.entries(this.editor.schema.nodes).map(([, value]) => value).filter((node) => (this.options.notAfter || []).concat(this.options.node).includes(node.name));
18
+ return [
19
+ new Plugin({
20
+ key: plugin,
21
+ appendTransaction: (_, __, state) => {
22
+ const { doc, tr, schema } = state;
23
+ const shouldInsertNodeAtEnd = plugin.getState(state);
24
+ const endPosition = doc.content.size;
25
+ const type = schema.nodes[this.options.node];
26
+ if (!shouldInsertNodeAtEnd) {
27
+ return;
28
+ }
29
+ return tr.insert(endPosition, type.create());
30
+ },
31
+ state: {
32
+ init: (_, state) => {
33
+ const lastNode = state.tr.doc.lastChild;
34
+ return !nodeEqualsType({ node: lastNode, types: disabledNodes });
35
+ },
36
+ apply: (tr, value) => {
37
+ if (!tr.docChanged) {
38
+ return value;
39
+ }
40
+ const lastNode = tr.doc.lastChild;
41
+ return !nodeEqualsType({ node: lastNode, types: disabledNodes });
42
+ }
43
+ }
44
+ })
45
+ ];
46
+ }
47
+ });
48
+ export {
49
+ TrailingNode
50
+ };
51
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/trailing-node/trailing-node.ts"],"sourcesContent":["import { Extension } from '@tiptap/core'\nimport { Node, NodeType } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\n\nfunction nodeEqualsType({ types, node }: { types: NodeType | NodeType[]; node: Node | null | undefined }) {\n return (node && Array.isArray(types) && types.includes(node.type)) || node?.type === types\n}\n\n/**\n * Extension based on:\n * - https://github.com/ueberdosis/tiptap/blob/v1/packages/tiptap-extensions/src/extensions/TrailingNode.js\n * - https://github.com/remirror/remirror/blob/e0f1bec4a1e8073ce8f5500d62193e52321155b9/packages/prosemirror-trailing-node/src/trailing-node-plugin.ts\n */\n\nexport interface TrailingNodeOptions {\n /**\n * The node type that should be inserted at the end of the document.\n * @note the node will always be added to the `notAfter` lists to\n * prevent an infinite loop.\n * @default 'paragraph'\n */\n node: string\n /**\n * The node types after which the trailing node should not be inserted.\n * @default ['paragraph']\n */\n notAfter?: string | string[]\n}\n\n/**\n * This extension allows you to add an extra node at the end of the document.\n * @see https://www.tiptap.dev/api/extensions/trailing-node\n */\nexport const TrailingNode = Extension.create<TrailingNodeOptions>({\n name: 'trailingNode',\n\n addOptions() {\n return {\n node: 'paragraph',\n notAfter: [],\n }\n },\n\n addProseMirrorPlugins() {\n const plugin = new PluginKey(this.name)\n const disabledNodes = Object.entries(this.editor.schema.nodes)\n .map(([, value]) => value)\n .filter(node => (this.options.notAfter || []).concat(this.options.node).includes(node.name))\n\n return [\n new Plugin({\n key: plugin,\n appendTransaction: (_, __, state) => {\n const { doc, tr, schema } = state\n const shouldInsertNodeAtEnd = plugin.getState(state)\n const endPosition = doc.content.size\n const type = schema.nodes[this.options.node]\n\n if (!shouldInsertNodeAtEnd) {\n return\n }\n\n return tr.insert(endPosition, type.create())\n },\n state: {\n init: (_, state) => {\n const lastNode = state.tr.doc.lastChild\n\n return !nodeEqualsType({ node: lastNode, types: disabledNodes })\n },\n apply: (tr, value) => {\n if (!tr.docChanged) {\n return value\n }\n\n const lastNode = tr.doc.lastChild\n\n return !nodeEqualsType({ node: lastNode, types: disabledNodes })\n },\n },\n }),\n ]\n },\n})\n"],"mappings":";AAAA,SAAS,iBAAiB;AAE1B,SAAS,QAAQ,iBAAiB;AAElC,SAAS,eAAe,EAAE,OAAO,KAAK,GAAoE;AACxG,SAAQ,QAAQ,MAAM,QAAQ,KAAK,KAAK,MAAM,SAAS,KAAK,IAAI,MAAM,6BAAM,UAAS;AACvF;AA2BO,IAAM,eAAe,UAAU,OAA4B;AAAA,EAChE,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU,CAAC;AAAA,IACb;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,UAAM,SAAS,IAAI,UAAU,KAAK,IAAI;AACtC,UAAM,gBAAgB,OAAO,QAAQ,KAAK,OAAO,OAAO,KAAK,EAC1D,IAAI,CAAC,CAAC,EAAE,KAAK,MAAM,KAAK,EACxB,OAAO,WAAS,KAAK,QAAQ,YAAY,CAAC,GAAG,OAAO,KAAK,QAAQ,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;AAE7F,WAAO;AAAA,MACL,IAAI,OAAO;AAAA,QACT,KAAK;AAAA,QACL,mBAAmB,CAAC,GAAG,IAAI,UAAU;AACnC,gBAAM,EAAE,KAAK,IAAI,OAAO,IAAI;AAC5B,gBAAM,wBAAwB,OAAO,SAAS,KAAK;AACnD,gBAAM,cAAc,IAAI,QAAQ;AAChC,gBAAM,OAAO,OAAO,MAAM,KAAK,QAAQ,IAAI;AAE3C,cAAI,CAAC,uBAAuB;AAC1B;AAAA,UACF;AAEA,iBAAO,GAAG,OAAO,aAAa,KAAK,OAAO,CAAC;AAAA,QAC7C;AAAA,QACA,OAAO;AAAA,UACL,MAAM,CAAC,GAAG,UAAU;AAClB,kBAAM,WAAW,MAAM,GAAG,IAAI;AAE9B,mBAAO,CAAC,eAAe,EAAE,MAAM,UAAU,OAAO,cAAc,CAAC;AAAA,UACjE;AAAA,UACA,OAAO,CAAC,IAAI,UAAU;AACpB,gBAAI,CAAC,GAAG,YAAY;AAClB,qBAAO;AAAA,YACT;AAEA,kBAAM,WAAW,GAAG,IAAI;AAExB,mBAAO,CAAC,eAAe,EAAE,MAAM,UAAU,OAAO,cAAc,CAAC;AAAA,UACjE;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;","names":[]}
package/package.json ADDED
@@ -0,0 +1,93 @@
1
+ {
2
+ "name": "@tiptap/extensions",
3
+ "description": "various extensions for tiptap",
4
+ "version": "3.0.0-next.3",
5
+ "homepage": "https://tiptap.dev",
6
+ "keywords": [
7
+ "tiptap",
8
+ "tiptap extension"
9
+ ],
10
+ "license": "MIT",
11
+ "funding": {
12
+ "type": "github",
13
+ "url": "https://github.com/sponsors/ueberdosis"
14
+ },
15
+ "type": "module",
16
+ "exports": {
17
+ ".": {
18
+ "types": {
19
+ "import": "./dist/index.d.ts",
20
+ "require": "./dist/index.d.cts"
21
+ },
22
+ "import": "./dist/index.js",
23
+ "require": "./dist/index.cjs"
24
+ },
25
+ "./drop-cursor": {
26
+ "types": {
27
+ "import": "./dist/drop-cursor/index.d.ts",
28
+ "require": "./dist/drop-cursor/index.d.cts"
29
+ },
30
+ "import": "./dist/drop-cursor/index.js",
31
+ "require": "./dist/drop-cursor/index.cjs"
32
+ },
33
+ "./focus": {
34
+ "types": {
35
+ "import": "./dist/focus/index.d.ts",
36
+ "require": "./dist/focus/index.d.cts"
37
+ },
38
+ "import": "./dist/focus/index.js",
39
+ "require": "./dist/focus/index.cjs"
40
+ },
41
+ "./gap-cursor": {
42
+ "types": {
43
+ "import": "./dist/gap-cursor/index.d.ts",
44
+ "require": "./dist/gap-cursor/index.d.cts"
45
+ },
46
+ "import": "./dist/gap-cursor/index.js",
47
+ "require": "./dist/gap-cursor/index.cjs"
48
+ },
49
+ "./selection": {
50
+ "types": {
51
+ "import": "./dist/selection/index.d.ts",
52
+ "require": "./dist/selection/index.d.cts"
53
+ },
54
+ "import": "./dist/selection/index.js",
55
+ "require": "./dist/selection/index.cjs"
56
+ },
57
+ "./trailing-node": {
58
+ "types": {
59
+ "import": "./dist/trailing-node/index.d.ts",
60
+ "require": "./dist/trailing-node/index.d.cts"
61
+ },
62
+ "import": "./dist/trailing-node/index.js",
63
+ "require": "./dist/trailing-node/index.cjs"
64
+ }
65
+ },
66
+ "main": "dist/index.cjs",
67
+ "module": "dist/index.js",
68
+ "types": "dist/index.d.ts",
69
+ "files": [
70
+ "src",
71
+ "dist"
72
+ ],
73
+ "devDependencies": {
74
+ "@tiptap/core": "^3.0.0-next.3",
75
+ "@tiptap/pm": "^3.0.0-next.3"
76
+ },
77
+ "peerDependencies": {
78
+ "@tiptap/core": "^3.0.0-next.3",
79
+ "@tiptap/pm": "^3.0.0-next.3"
80
+ },
81
+ "repository": {
82
+ "type": "git",
83
+ "url": "https://github.com/ueberdosis/tiptap",
84
+ "directory": "packages/extension"
85
+ },
86
+ "publishConfig": {
87
+ "access": "public"
88
+ },
89
+ "scripts": {
90
+ "build": "tsup",
91
+ "lint": "prettier ./src/ --check && eslint --cache --quiet --no-error-on-unmatched-pattern ./src/"
92
+ }
93
+ }
@@ -0,0 +1,47 @@
1
+ import { Extension } from '@tiptap/core'
2
+ import { dropCursor } from '@tiptap/pm/dropcursor'
3
+
4
+ export interface DropcursorOptions {
5
+ /**
6
+ * The color of the drop cursor
7
+ * @default 'currentColor'
8
+ * @example 'red'
9
+ */
10
+ color: string | undefined
11
+
12
+ /**
13
+ * The width of the drop cursor
14
+ * @default 1
15
+ * @example 2
16
+ */
17
+ width: number | undefined
18
+
19
+ /**
20
+ * The class of the drop cursor
21
+ * @default undefined
22
+ * @example 'drop-cursor'
23
+ */
24
+ class: string | undefined
25
+ }
26
+
27
+ /**
28
+ * This extension allows you to add a drop cursor to your editor.
29
+ * A drop cursor is a line that appears when you drag and drop content
30
+ * in-between nodes.
31
+ * @see https://tiptap.dev/api/extensions/dropcursor
32
+ */
33
+ export const Dropcursor = Extension.create<DropcursorOptions>({
34
+ name: 'dropCursor',
35
+
36
+ addOptions() {
37
+ return {
38
+ color: 'currentColor',
39
+ width: 1,
40
+ class: undefined,
41
+ }
42
+ },
43
+
44
+ addProseMirrorPlugins() {
45
+ return [dropCursor(this.options)]
46
+ },
47
+ })
@@ -0,0 +1 @@
1
+ export * from './drop-cursor.js'
@@ -0,0 +1,110 @@
1
+ import { Extension } from '@tiptap/core'
2
+ import { Plugin, PluginKey } from '@tiptap/pm/state'
3
+ import { Decoration, DecorationSet } from '@tiptap/pm/view'
4
+
5
+ export interface FocusOptions {
6
+ /**
7
+ * The class name that should be added to the focused node.
8
+ * @default 'has-focus'
9
+ * @example 'is-focused'
10
+ */
11
+ className: string
12
+
13
+ /**
14
+ * The mode by which the focused node is determined.
15
+ * - All: All nodes are marked as focused.
16
+ * - Deepest: Only the deepest node is marked as focused.
17
+ * - Shallowest: Only the shallowest node is marked as focused.
18
+ *
19
+ * @default 'all'
20
+ * @example 'deepest'
21
+ * @example 'shallowest'
22
+ */
23
+ mode: 'all' | 'deepest' | 'shallowest'
24
+ }
25
+
26
+ /**
27
+ * This extension allows you to add a class to the focused node.
28
+ * @see https://www.tiptap.dev/api/extensions/focus
29
+ */
30
+ export const Focus = Extension.create<FocusOptions>({
31
+ name: 'focus',
32
+
33
+ addOptions() {
34
+ return {
35
+ className: 'has-focus',
36
+ mode: 'all',
37
+ }
38
+ },
39
+
40
+ addProseMirrorPlugins() {
41
+ return [
42
+ new Plugin({
43
+ key: new PluginKey('focus'),
44
+ props: {
45
+ decorations: ({ doc, selection }) => {
46
+ const { isEditable, isFocused } = this.editor
47
+ const { anchor } = selection
48
+ const decorations: Decoration[] = []
49
+
50
+ if (!isEditable || !isFocused) {
51
+ return DecorationSet.create(doc, [])
52
+ }
53
+
54
+ // Maximum Levels
55
+ let maxLevels = 0
56
+
57
+ if (this.options.mode === 'deepest') {
58
+ doc.descendants((node, pos) => {
59
+ if (node.isText) {
60
+ return
61
+ }
62
+
63
+ const isCurrent = anchor >= pos && anchor <= pos + node.nodeSize - 1
64
+
65
+ if (!isCurrent) {
66
+ return false
67
+ }
68
+
69
+ maxLevels += 1
70
+ })
71
+ }
72
+
73
+ // Loop through current
74
+ let currentLevel = 0
75
+
76
+ doc.descendants((node, pos) => {
77
+ if (node.isText) {
78
+ return false
79
+ }
80
+
81
+ const isCurrent = anchor >= pos && anchor <= pos + node.nodeSize - 1
82
+
83
+ if (!isCurrent) {
84
+ return false
85
+ }
86
+
87
+ currentLevel += 1
88
+
89
+ const outOfScope =
90
+ (this.options.mode === 'deepest' && maxLevels - currentLevel > 0) ||
91
+ (this.options.mode === 'shallowest' && currentLevel > 1)
92
+
93
+ if (outOfScope) {
94
+ return this.options.mode === 'deepest'
95
+ }
96
+
97
+ decorations.push(
98
+ Decoration.node(pos, pos + node.nodeSize, {
99
+ class: this.options.className,
100
+ }),
101
+ )
102
+ })
103
+
104
+ return DecorationSet.create(doc, decorations)
105
+ },
106
+ },
107
+ }),
108
+ ]
109
+ },
110
+ })
@@ -0,0 +1 @@
1
+ export * from './focus.js'
@@ -0,0 +1,46 @@
1
+ import { callOrReturn, Extension, getExtensionField, ParentConfig } from '@tiptap/core'
2
+ import { gapCursor } from '@tiptap/pm/gapcursor'
3
+
4
+ declare module '@tiptap/core' {
5
+ interface NodeConfig<Options, Storage> {
6
+ /**
7
+ * A function to determine whether the gap cursor is allowed at the current position. Must return `true` or `false`.
8
+ * @default null
9
+ */
10
+ allowGapCursor?:
11
+ | boolean
12
+ | null
13
+ | ((this: {
14
+ name: string
15
+ options: Options
16
+ storage: Storage
17
+ parent: ParentConfig<NodeConfig<Options>>['allowGapCursor']
18
+ }) => boolean | null)
19
+ }
20
+ }
21
+
22
+ /**
23
+ * This extension allows you to add a gap cursor to your editor.
24
+ * A gap cursor is a cursor that appears when you click on a place
25
+ * where no content is present, for example inbetween nodes.
26
+ * @see https://tiptap.dev/api/extensions/gapcursor
27
+ */
28
+ export const Gapcursor = Extension.create({
29
+ name: 'gapCursor',
30
+
31
+ addProseMirrorPlugins() {
32
+ return [gapCursor()]
33
+ },
34
+
35
+ extendNodeSchema(extension) {
36
+ const context = {
37
+ name: extension.name,
38
+ options: extension.options,
39
+ storage: extension.storage,
40
+ }
41
+
42
+ return {
43
+ allowGapCursor: callOrReturn(getExtensionField(extension, 'allowGapCursor', context)) ?? null,
44
+ }
45
+ },
46
+ })
@@ -0,0 +1 @@
1
+ export * from './gap-cursor.js'
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ export * from './drop-cursor/index.js'
2
+ export * from './focus/index.js'
3
+ export * from './gap-cursor/index.js'
4
+ export * from './selection/index.js'
5
+ export * from './trailing-node/index.js'
@@ -0,0 +1 @@
1
+ export * from './selection.js'
@@ -0,0 +1,51 @@
1
+ import { Extension } from '@tiptap/core'
2
+ import { Plugin, PluginKey } from '@tiptap/pm/state'
3
+ import { Decoration, DecorationSet } from '@tiptap/pm/view'
4
+
5
+ export type SelectionOptions = {
6
+ /**
7
+ * The class name that should be added to the selected text.
8
+ * @default 'selection'
9
+ * @example 'is-selected'
10
+ */
11
+ className: string
12
+ }
13
+
14
+ /**
15
+ * This extension allows you to add a class to the selected text.
16
+ * @see https://www.tiptap.dev/api/extensions/selection
17
+ */
18
+ export const Selection = Extension.create({
19
+ name: 'selection',
20
+
21
+ addOptions() {
22
+ return {
23
+ className: 'selection',
24
+ }
25
+ },
26
+
27
+ addProseMirrorPlugins() {
28
+ const { editor, options } = this
29
+
30
+ return [
31
+ new Plugin({
32
+ key: new PluginKey('selection'),
33
+ props: {
34
+ decorations(state) {
35
+ if (state.selection.empty || editor.isFocused) {
36
+ return null
37
+ }
38
+
39
+ return DecorationSet.create(state.doc, [
40
+ Decoration.inline(state.selection.from, state.selection.to, {
41
+ class: options.className,
42
+ }),
43
+ ])
44
+ },
45
+ },
46
+ }),
47
+ ]
48
+ },
49
+ })
50
+
51
+ export default Selection
@@ -0,0 +1 @@
1
+ export * from './trailing-node.js'
@@ -0,0 +1,84 @@
1
+ import { Extension } from '@tiptap/core'
2
+ import { Node, NodeType } from '@tiptap/pm/model'
3
+ import { Plugin, PluginKey } from '@tiptap/pm/state'
4
+
5
+ function nodeEqualsType({ types, node }: { types: NodeType | NodeType[]; node: Node | null | undefined }) {
6
+ return (node && Array.isArray(types) && types.includes(node.type)) || node?.type === types
7
+ }
8
+
9
+ /**
10
+ * Extension based on:
11
+ * - https://github.com/ueberdosis/tiptap/blob/v1/packages/tiptap-extensions/src/extensions/TrailingNode.js
12
+ * - https://github.com/remirror/remirror/blob/e0f1bec4a1e8073ce8f5500d62193e52321155b9/packages/prosemirror-trailing-node/src/trailing-node-plugin.ts
13
+ */
14
+
15
+ export interface TrailingNodeOptions {
16
+ /**
17
+ * The node type that should be inserted at the end of the document.
18
+ * @note the node will always be added to the `notAfter` lists to
19
+ * prevent an infinite loop.
20
+ * @default 'paragraph'
21
+ */
22
+ node: string
23
+ /**
24
+ * The node types after which the trailing node should not be inserted.
25
+ * @default ['paragraph']
26
+ */
27
+ notAfter?: string | string[]
28
+ }
29
+
30
+ /**
31
+ * This extension allows you to add an extra node at the end of the document.
32
+ * @see https://www.tiptap.dev/api/extensions/trailing-node
33
+ */
34
+ export const TrailingNode = Extension.create<TrailingNodeOptions>({
35
+ name: 'trailingNode',
36
+
37
+ addOptions() {
38
+ return {
39
+ node: 'paragraph',
40
+ notAfter: [],
41
+ }
42
+ },
43
+
44
+ addProseMirrorPlugins() {
45
+ const plugin = new PluginKey(this.name)
46
+ const disabledNodes = Object.entries(this.editor.schema.nodes)
47
+ .map(([, value]) => value)
48
+ .filter(node => (this.options.notAfter || []).concat(this.options.node).includes(node.name))
49
+
50
+ return [
51
+ new Plugin({
52
+ key: plugin,
53
+ appendTransaction: (_, __, state) => {
54
+ const { doc, tr, schema } = state
55
+ const shouldInsertNodeAtEnd = plugin.getState(state)
56
+ const endPosition = doc.content.size
57
+ const type = schema.nodes[this.options.node]
58
+
59
+ if (!shouldInsertNodeAtEnd) {
60
+ return
61
+ }
62
+
63
+ return tr.insert(endPosition, type.create())
64
+ },
65
+ state: {
66
+ init: (_, state) => {
67
+ const lastNode = state.tr.doc.lastChild
68
+
69
+ return !nodeEqualsType({ node: lastNode, types: disabledNodes })
70
+ },
71
+ apply: (tr, value) => {
72
+ if (!tr.docChanged) {
73
+ return value
74
+ }
75
+
76
+ const lastNode = tr.doc.lastChild
77
+
78
+ return !nodeEqualsType({ node: lastNode, types: disabledNodes })
79
+ },
80
+ },
81
+ }),
82
+ ]
83
+ },
84
+ })