@tiptap/extension-mention 2.0.0-beta.21 → 2.0.0-beta.211

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/README.md CHANGED
@@ -7,8 +7,8 @@
7
7
  ## Introduction
8
8
  tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) – a toolkit for building rich text WYSIWYG editors, which is already in use at many well-known companies such as *New York Times*, *The Guardian* or *Atlassian*.
9
9
 
10
- ## Offical Documentation
10
+ ## Official Documentation
11
11
  Documentation can be found on the [tiptap website](https://tiptap.dev).
12
12
 
13
13
  ## License
14
- tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md).
14
+ tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap/blob/main/LICENSE.md).
package/dist/index.cjs ADDED
@@ -0,0 +1,137 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }// src/mention.ts
2
+ var _core = require('@tiptap/core');
3
+ var _state = require('@tiptap/pm/state');
4
+ var _suggestion = require('@tiptap/suggestion'); var _suggestion2 = _interopRequireDefault(_suggestion);
5
+ var MentionPluginKey = new (0, _state.PluginKey)("mention");
6
+ var Mention = _core.Node.create({
7
+ name: "mention",
8
+ addOptions() {
9
+ return {
10
+ HTMLAttributes: {},
11
+ renderLabel({ options, node }) {
12
+ var _a;
13
+ return `${options.suggestion.char}${(_a = node.attrs.label) != null ? _a : node.attrs.id}`;
14
+ },
15
+ suggestion: {
16
+ char: "@",
17
+ pluginKey: MentionPluginKey,
18
+ command: ({ editor, range, props }) => {
19
+ var _a, _b;
20
+ const nodeAfter = editor.view.state.selection.$to.nodeAfter;
21
+ const overrideSpace = (_a = nodeAfter == null ? void 0 : nodeAfter.text) == null ? void 0 : _a.startsWith(" ");
22
+ if (overrideSpace) {
23
+ range.to += 1;
24
+ }
25
+ editor.chain().focus().insertContentAt(range, [
26
+ {
27
+ type: this.name,
28
+ attrs: props
29
+ },
30
+ {
31
+ type: "text",
32
+ text: " "
33
+ }
34
+ ]).run();
35
+ (_b = window.getSelection()) == null ? void 0 : _b.collapseToEnd();
36
+ },
37
+ allow: ({ state, range }) => {
38
+ const $from = state.doc.resolve(range.from);
39
+ const type = state.schema.nodes[this.name];
40
+ const allow = !!$from.parent.type.contentMatch.matchType(type);
41
+ return allow;
42
+ }
43
+ }
44
+ };
45
+ },
46
+ group: "inline",
47
+ inline: true,
48
+ selectable: false,
49
+ atom: true,
50
+ addAttributes() {
51
+ return {
52
+ id: {
53
+ default: null,
54
+ parseHTML: (element) => element.getAttribute("data-id"),
55
+ renderHTML: (attributes) => {
56
+ if (!attributes.id) {
57
+ return {};
58
+ }
59
+ return {
60
+ "data-id": attributes.id
61
+ };
62
+ }
63
+ },
64
+ label: {
65
+ default: null,
66
+ parseHTML: (element) => element.getAttribute("data-label"),
67
+ renderHTML: (attributes) => {
68
+ if (!attributes.label) {
69
+ return {};
70
+ }
71
+ return {
72
+ "data-label": attributes.label
73
+ };
74
+ }
75
+ }
76
+ };
77
+ },
78
+ parseHTML() {
79
+ return [
80
+ {
81
+ tag: `span[data-type="${this.name}"]`
82
+ }
83
+ ];
84
+ },
85
+ renderHTML({ node, HTMLAttributes }) {
86
+ return [
87
+ "span",
88
+ _core.mergeAttributes.call(void 0, { "data-type": this.name }, this.options.HTMLAttributes, HTMLAttributes),
89
+ this.options.renderLabel({
90
+ options: this.options,
91
+ node
92
+ })
93
+ ];
94
+ },
95
+ renderText({ node }) {
96
+ return this.options.renderLabel({
97
+ options: this.options,
98
+ node
99
+ });
100
+ },
101
+ addKeyboardShortcuts() {
102
+ return {
103
+ Backspace: () => this.editor.commands.command(({ tr, state }) => {
104
+ let isMention = false;
105
+ const { selection } = state;
106
+ const { empty, anchor } = selection;
107
+ if (!empty) {
108
+ return false;
109
+ }
110
+ state.doc.nodesBetween(anchor - 1, anchor, (node, pos) => {
111
+ if (node.type.name === this.name) {
112
+ isMention = true;
113
+ tr.insertText(this.options.suggestion.char || "", pos, pos + node.nodeSize);
114
+ return false;
115
+ }
116
+ });
117
+ return isMention;
118
+ })
119
+ };
120
+ },
121
+ addProseMirrorPlugins() {
122
+ return [
123
+ _suggestion2.default.call(void 0, {
124
+ editor: this.editor,
125
+ ...this.options.suggestion
126
+ })
127
+ ];
128
+ }
129
+ });
130
+
131
+ // src/index.ts
132
+ var src_default = Mention;
133
+
134
+
135
+
136
+
137
+ exports.Mention = Mention; exports.MentionPluginKey = MentionPluginKey; exports.default = src_default;
@@ -0,0 +1,17 @@
1
+ import { Node as Node$1 } from '@tiptap/core';
2
+ import { Node } from '@tiptap/pm/model';
3
+ import { PluginKey } from '@tiptap/pm/state';
4
+ import { SuggestionOptions } from '@tiptap/suggestion';
5
+
6
+ declare type MentionOptions = {
7
+ HTMLAttributes: Record<string, any>;
8
+ renderLabel: (props: {
9
+ options: MentionOptions;
10
+ node: Node;
11
+ }) => string;
12
+ suggestion: Omit<SuggestionOptions, 'editor'>;
13
+ };
14
+ declare const MentionPluginKey: PluginKey<any>;
15
+ declare const Mention: Node$1<MentionOptions, any>;
16
+
17
+ export { Mention, MentionOptions, MentionPluginKey, Mention as default };
package/dist/index.js ADDED
@@ -0,0 +1,137 @@
1
+ // src/mention.ts
2
+ import { mergeAttributes, Node } from "@tiptap/core";
3
+ import { PluginKey } from "@tiptap/pm/state";
4
+ import Suggestion from "@tiptap/suggestion";
5
+ var MentionPluginKey = new PluginKey("mention");
6
+ var Mention = Node.create({
7
+ name: "mention",
8
+ addOptions() {
9
+ return {
10
+ HTMLAttributes: {},
11
+ renderLabel({ options, node }) {
12
+ var _a;
13
+ return `${options.suggestion.char}${(_a = node.attrs.label) != null ? _a : node.attrs.id}`;
14
+ },
15
+ suggestion: {
16
+ char: "@",
17
+ pluginKey: MentionPluginKey,
18
+ command: ({ editor, range, props }) => {
19
+ var _a, _b;
20
+ const nodeAfter = editor.view.state.selection.$to.nodeAfter;
21
+ const overrideSpace = (_a = nodeAfter == null ? void 0 : nodeAfter.text) == null ? void 0 : _a.startsWith(" ");
22
+ if (overrideSpace) {
23
+ range.to += 1;
24
+ }
25
+ editor.chain().focus().insertContentAt(range, [
26
+ {
27
+ type: this.name,
28
+ attrs: props
29
+ },
30
+ {
31
+ type: "text",
32
+ text: " "
33
+ }
34
+ ]).run();
35
+ (_b = window.getSelection()) == null ? void 0 : _b.collapseToEnd();
36
+ },
37
+ allow: ({ state, range }) => {
38
+ const $from = state.doc.resolve(range.from);
39
+ const type = state.schema.nodes[this.name];
40
+ const allow = !!$from.parent.type.contentMatch.matchType(type);
41
+ return allow;
42
+ }
43
+ }
44
+ };
45
+ },
46
+ group: "inline",
47
+ inline: true,
48
+ selectable: false,
49
+ atom: true,
50
+ addAttributes() {
51
+ return {
52
+ id: {
53
+ default: null,
54
+ parseHTML: (element) => element.getAttribute("data-id"),
55
+ renderHTML: (attributes) => {
56
+ if (!attributes.id) {
57
+ return {};
58
+ }
59
+ return {
60
+ "data-id": attributes.id
61
+ };
62
+ }
63
+ },
64
+ label: {
65
+ default: null,
66
+ parseHTML: (element) => element.getAttribute("data-label"),
67
+ renderHTML: (attributes) => {
68
+ if (!attributes.label) {
69
+ return {};
70
+ }
71
+ return {
72
+ "data-label": attributes.label
73
+ };
74
+ }
75
+ }
76
+ };
77
+ },
78
+ parseHTML() {
79
+ return [
80
+ {
81
+ tag: `span[data-type="${this.name}"]`
82
+ }
83
+ ];
84
+ },
85
+ renderHTML({ node, HTMLAttributes }) {
86
+ return [
87
+ "span",
88
+ mergeAttributes({ "data-type": this.name }, this.options.HTMLAttributes, HTMLAttributes),
89
+ this.options.renderLabel({
90
+ options: this.options,
91
+ node
92
+ })
93
+ ];
94
+ },
95
+ renderText({ node }) {
96
+ return this.options.renderLabel({
97
+ options: this.options,
98
+ node
99
+ });
100
+ },
101
+ addKeyboardShortcuts() {
102
+ return {
103
+ Backspace: () => this.editor.commands.command(({ tr, state }) => {
104
+ let isMention = false;
105
+ const { selection } = state;
106
+ const { empty, anchor } = selection;
107
+ if (!empty) {
108
+ return false;
109
+ }
110
+ state.doc.nodesBetween(anchor - 1, anchor, (node, pos) => {
111
+ if (node.type.name === this.name) {
112
+ isMention = true;
113
+ tr.insertText(this.options.suggestion.char || "", pos, pos + node.nodeSize);
114
+ return false;
115
+ }
116
+ });
117
+ return isMention;
118
+ })
119
+ };
120
+ },
121
+ addProseMirrorPlugins() {
122
+ return [
123
+ Suggestion({
124
+ editor: this.editor,
125
+ ...this.options.suggestion
126
+ })
127
+ ];
128
+ }
129
+ });
130
+
131
+ // src/index.ts
132
+ var src_default = Mention;
133
+ export {
134
+ Mention,
135
+ MentionPluginKey,
136
+ src_default as default
137
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tiptap/extension-mention",
3
3
  "description": "mention extension for tiptap",
4
- "version": "2.0.0-beta.21",
4
+ "version": "2.0.0-beta.211",
5
5
  "homepage": "https://tiptap.dev",
6
6
  "keywords": [
7
7
  "tiptap",
@@ -12,20 +12,48 @@
12
12
  "type": "github",
13
13
  "url": "https://github.com/sponsors/ueberdosis"
14
14
  },
15
- "main": "dist/tiptap-extension-mention.cjs.js",
16
- "umd": "dist/tiptap-extension-mention.umd.js",
17
- "module": "dist/tiptap-extension-mention.esm.js",
18
- "unpkg": "dist/tiptap-extension-mention.bundle.umd.min.js",
19
- "types": "dist/packages/extension-mention/src/index.d.ts",
15
+ "type": "module",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js",
20
+ "require": "./dist/index.cjs"
21
+ }
22
+ },
23
+ "main": "dist/index.cjs",
24
+ "module": "dist/index.js",
25
+ "types": "dist/index.d.ts",
20
26
  "files": [
21
27
  "src",
22
28
  "dist"
23
29
  ],
24
30
  "peerDependencies": {
25
- "@tiptap/core": "^2.0.0-beta.1"
31
+ "@tiptap/core": "^2.0.0-beta.209",
32
+ "@tiptap/pm": "^2.0.0-beta.209",
33
+ "@tiptap/suggestion": "^2.0.0-beta.209"
34
+ },
35
+ "devDependencies": {
36
+ "@tiptap/core": "^2.0.0-beta.211",
37
+ "@tiptap/pm": "^2.0.0-beta.211",
38
+ "@tiptap/suggestion": "^2.0.0-beta.211"
39
+ },
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/ueberdosis/tiptap",
43
+ "directory": "packages/extension-mention"
26
44
  },
27
- "dependencies": {
28
- "@tiptap/suggestion": "^2.0.0-beta.21"
45
+ "scripts": {
46
+ "build": "tsup"
29
47
  },
30
- "gitHead": "113d50b814c8860465c8bb16ae2bee3097689016"
48
+ "tsup": {
49
+ "entry": [
50
+ "src/index.ts"
51
+ ],
52
+ "dts": true,
53
+ "splitting": true,
54
+ "format": [
55
+ "esm",
56
+ "cjs"
57
+ ]
58
+ }
31
59
  }
package/src/mention.ts CHANGED
@@ -1,32 +1,64 @@
1
- import { Node, mergeAttributes } from '@tiptap/core'
1
+ import { mergeAttributes, Node } from '@tiptap/core'
2
+ import { Node as ProseMirrorNode } from '@tiptap/pm/model'
3
+ import { PluginKey } from '@tiptap/pm/state'
2
4
  import Suggestion, { SuggestionOptions } from '@tiptap/suggestion'
3
5
 
4
6
  export type MentionOptions = {
5
- HTMLAttributes: {
6
- [key: string]: any,
7
- },
8
- suggestion: Omit<SuggestionOptions, 'editor'>,
7
+ HTMLAttributes: Record<string, any>
8
+ renderLabel: (props: { options: MentionOptions; node: ProseMirrorNode }) => string
9
+ suggestion: Omit<SuggestionOptions, 'editor'>
9
10
  }
10
11
 
12
+ export const MentionPluginKey = new PluginKey('mention')
13
+
11
14
  export const Mention = Node.create<MentionOptions>({
12
15
  name: 'mention',
13
16
 
14
- defaultOptions: {
15
- HTMLAttributes: {},
16
- suggestion: {
17
- char: '@',
18
- command: ({ editor, range, props }) => {
19
- editor
20
- .chain()
21
- .focus()
22
- .replaceRange(range, 'mention', props)
23
- .insertContent(' ')
24
- .run()
17
+ addOptions() {
18
+ return {
19
+ HTMLAttributes: {},
20
+ renderLabel({ options, node }) {
21
+ return `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`
25
22
  },
26
- allow: ({ editor, range }) => {
27
- return editor.can().replaceRange(range, 'mention')
23
+ suggestion: {
24
+ char: '@',
25
+ pluginKey: MentionPluginKey,
26
+ command: ({ editor, range, props }) => {
27
+ // increase range.to by one when the next node is of type "text"
28
+ // and starts with a space character
29
+ const nodeAfter = editor.view.state.selection.$to.nodeAfter
30
+ const overrideSpace = nodeAfter?.text?.startsWith(' ')
31
+
32
+ if (overrideSpace) {
33
+ range.to += 1
34
+ }
35
+
36
+ editor
37
+ .chain()
38
+ .focus()
39
+ .insertContentAt(range, [
40
+ {
41
+ type: this.name,
42
+ attrs: props,
43
+ },
44
+ {
45
+ type: 'text',
46
+ text: ' ',
47
+ },
48
+ ])
49
+ .run()
50
+
51
+ window.getSelection()?.collapseToEnd()
52
+ },
53
+ allow: ({ state, range }) => {
54
+ const $from = state.doc.resolve(range.from)
55
+ const type = state.schema.nodes[this.name]
56
+ const allow = !!$from.parent.type.contentMatch.matchType(type)
57
+
58
+ return allow
59
+ },
28
60
  },
29
- },
61
+ }
30
62
  },
31
63
 
32
64
  group: 'inline',
@@ -41,18 +73,28 @@ export const Mention = Node.create<MentionOptions>({
41
73
  return {
42
74
  id: {
43
75
  default: null,
44
- parseHTML: element => {
76
+ parseHTML: element => element.getAttribute('data-id'),
77
+ renderHTML: attributes => {
78
+ if (!attributes.id) {
79
+ return {}
80
+ }
81
+
45
82
  return {
46
- id: element.getAttribute('data-mention'),
83
+ 'data-id': attributes.id,
47
84
  }
48
85
  },
86
+ },
87
+
88
+ label: {
89
+ default: null,
90
+ parseHTML: element => element.getAttribute('data-label'),
49
91
  renderHTML: attributes => {
50
- if (!attributes.id) {
92
+ if (!attributes.label) {
51
93
  return {}
52
94
  }
53
95
 
54
96
  return {
55
- 'data-mention': attributes.id,
97
+ 'data-label': attributes.label,
56
98
  }
57
99
  },
58
100
  },
@@ -62,17 +104,27 @@ export const Mention = Node.create<MentionOptions>({
62
104
  parseHTML() {
63
105
  return [
64
106
  {
65
- tag: 'span[data-mention]',
107
+ tag: `span[data-type="${this.name}"]`,
66
108
  },
67
109
  ]
68
110
  },
69
111
 
70
112
  renderHTML({ node, HTMLAttributes }) {
71
- return ['span', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), `@${node.attrs.id}`]
113
+ return [
114
+ 'span',
115
+ mergeAttributes({ 'data-type': this.name }, this.options.HTMLAttributes, HTMLAttributes),
116
+ this.options.renderLabel({
117
+ options: this.options,
118
+ node,
119
+ }),
120
+ ]
72
121
  },
73
122
 
74
123
  renderText({ node }) {
75
- return `@${node.attrs.id}`
124
+ return this.options.renderLabel({
125
+ options: this.options,
126
+ node,
127
+ })
76
128
  },
77
129
 
78
130
  addKeyboardShortcuts() {
@@ -87,7 +139,7 @@ export const Mention = Node.create<MentionOptions>({
87
139
  }
88
140
 
89
141
  state.doc.nodesBetween(anchor - 1, anchor, (node, pos) => {
90
- if (node.type.name === 'mention') {
142
+ if (node.type.name === this.name) {
91
143
  isMention = true
92
144
  tr.insertText(this.options.suggestion.char || '', pos, pos + node.nodeSize)
93
145