@livepreso/react-plugin-textfield 0.2.0 → 0.2.1
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/CHANGELOG.json +15 -0
- package/CHANGELOG.md +9 -1
- package/components/tiptap/token/Token.js +47 -5
- package/constants.js +5 -1
- package/index.js +19 -6
- package/package.json +1 -1
package/CHANGELOG.json
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@livepreso/react-plugin-textfield",
|
|
3
3
|
"entries": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.2.1",
|
|
6
|
+
"tag": "@livepreso/react-plugin-textfield_v0.2.1",
|
|
7
|
+
"date": "Sun, 23 Nov 2025 23:46:44 GMT",
|
|
8
|
+
"comments": {
|
|
9
|
+
"patch": [
|
|
10
|
+
{
|
|
11
|
+
"comment": "Burst all tokens when opening saved token state into a burst editable state."
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"comment": "Render token option before clear_formatting in simple toolbar config spec"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
},
|
|
4
19
|
{
|
|
5
20
|
"version": "0.2.0",
|
|
6
21
|
"tag": "@livepreso/react-plugin-textfield_v0.2.0",
|
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
# Change Log - @livepreso/react-plugin-textfield
|
|
2
2
|
|
|
3
|
-
This log was last generated on
|
|
3
|
+
This log was last generated on Sun, 23 Nov 2025 23:46:44 GMT and should not be manually modified.
|
|
4
|
+
|
|
5
|
+
## 0.2.1
|
|
6
|
+
Sun, 23 Nov 2025 23:46:44 GMT
|
|
7
|
+
|
|
8
|
+
### Patches
|
|
9
|
+
|
|
10
|
+
- Burst all tokens when opening saved token state into a burst editable state.
|
|
11
|
+
- Render token option before clear_formatting in simple toolbar config spec
|
|
4
12
|
|
|
5
13
|
## 0.2.0
|
|
6
14
|
Wed, 19 Nov 2025 03:51:23 GMT
|
|
@@ -3,8 +3,47 @@ import style from "./Token.module.scss";
|
|
|
3
3
|
import classNames from "classnames";
|
|
4
4
|
import { generateSuggestionConfiguration, TOKEN_TRIGGER_CHAR } from "./utils";
|
|
5
5
|
import { mergeAttributes } from "@tiptap/core";
|
|
6
|
+
import { NODE_TYPES } from "../../../constants";
|
|
6
7
|
|
|
7
|
-
const
|
|
8
|
+
export const convertTokensToTextNodes = (editor, options) => {
|
|
9
|
+
const nodesToReplace = [];
|
|
10
|
+
|
|
11
|
+
// Using transaction instead of run() so all changes
|
|
12
|
+
// are made before application. When I did everything one
|
|
13
|
+
// run() after the other, replacements got jumbled as the
|
|
14
|
+
// positions changed after each replacement.
|
|
15
|
+
let tr = editor.state.tr;
|
|
16
|
+
|
|
17
|
+
editor.state.doc.descendants((node, pos) => {
|
|
18
|
+
if (node.type.name === NODE_TYPES.TOKEN) {
|
|
19
|
+
nodesToReplace.push({ node, pos });
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Sort by position to avoid position shifts
|
|
24
|
+
nodesToReplace.sort((a, b) => b.pos - a.pos);
|
|
25
|
+
|
|
26
|
+
nodesToReplace.forEach(({ node, pos }) => {
|
|
27
|
+
const { render: tokenRender = () => "" } =
|
|
28
|
+
options.tokens.find((token) => token.id === node.attrs.id) || {};
|
|
29
|
+
|
|
30
|
+
const tokenSummary = `${TOKEN_TRIGGER_CHAR}${
|
|
31
|
+
node.attrs.label ?? node.attrs.id
|
|
32
|
+
}`;
|
|
33
|
+
|
|
34
|
+
const content =
|
|
35
|
+
options.isEditable && options.usePlaceholder
|
|
36
|
+
? tokenSummary
|
|
37
|
+
: tokenRender();
|
|
38
|
+
|
|
39
|
+
const endPos = pos + editor.state.doc.nodeAt(pos).nodeSize;
|
|
40
|
+
const textNode = editor.state.schema.text(content);
|
|
41
|
+
|
|
42
|
+
tr.replaceWith(pos, endPos, textNode);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
editor.view.dispatch(tr);
|
|
46
|
+
};
|
|
8
47
|
|
|
9
48
|
const TokenBase = Mention.extend({
|
|
10
49
|
name: "token",
|
|
@@ -62,7 +101,7 @@ const TokenBase = Mention.extend({
|
|
|
62
101
|
: render();
|
|
63
102
|
|
|
64
103
|
const editableNode = document.createElement("span");
|
|
65
|
-
editableNode.setAttribute("contenteditable",
|
|
104
|
+
editableNode.setAttribute("contenteditable", options.fieldIsEditable);
|
|
66
105
|
|
|
67
106
|
const dom = options.burstOnCreate ? editableNode : tokenDOM;
|
|
68
107
|
const contentDOM = dom.appendChild(document.createTextNode(content));
|
|
@@ -79,7 +118,7 @@ const TokenBase = Mention.extend({
|
|
|
79
118
|
.focus()
|
|
80
119
|
.setNodeSelection(pos)
|
|
81
120
|
.insertContentAt({ from: pos, to: endPos }, content)
|
|
82
|
-
.toggleNode(
|
|
121
|
+
.toggleNode(NODE_TYPES.TOKEN, "text")
|
|
83
122
|
.run();
|
|
84
123
|
};
|
|
85
124
|
|
|
@@ -93,8 +132,11 @@ const TokenBase = Mention.extend({
|
|
|
93
132
|
|
|
94
133
|
if (options.burstOnCreate) {
|
|
95
134
|
// this will be run, once, after the node is created
|
|
96
|
-
// after which, the Token will no longer exist
|
|
97
|
-
|
|
135
|
+
// after which, the Token will no longer exist.
|
|
136
|
+
// Only run if currently editing and adding tokens (ie. not on editor load)
|
|
137
|
+
if (editor.isFocused) {
|
|
138
|
+
setTimeout(() => convertToTextNode(), 50);
|
|
139
|
+
}
|
|
98
140
|
}
|
|
99
141
|
return {
|
|
100
142
|
dom,
|
package/constants.js
CHANGED
|
@@ -9,6 +9,10 @@ export const TOOLBAR_COMPONENT_TYPES = Object.freeze({
|
|
|
9
9
|
CUSTOM: "custom",
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
+
export const NODE_TYPES = Object.freeze({
|
|
13
|
+
TOKEN: "token",
|
|
14
|
+
});
|
|
15
|
+
|
|
12
16
|
/**
|
|
13
17
|
* What a toolbar option can affect, and where they will appear in the editor.
|
|
14
18
|
*
|
|
@@ -195,8 +199,8 @@ export const MINIMAL_TOOLBAR_CONFIG = [UNDO_REDO];
|
|
|
195
199
|
export const SIMPLE_TOOLBAR_CONFIG = [
|
|
196
200
|
UNDO_REDO,
|
|
197
201
|
TEXT_STYLE,
|
|
198
|
-
CLEAR_FORMATTING,
|
|
199
202
|
TOKEN,
|
|
203
|
+
CLEAR_FORMATTING,
|
|
200
204
|
];
|
|
201
205
|
|
|
202
206
|
export const SIMPLE_TABLE_TOOLBAR_CONFIG = [
|
package/index.js
CHANGED
|
@@ -41,7 +41,10 @@ import { TableCellMenu } from "./components/TableCellMenu.js";
|
|
|
41
41
|
import style from "./index.module.scss";
|
|
42
42
|
import { generateExtensionsFromTag } from "./utils/generateCustomExtensions.js";
|
|
43
43
|
import debounce from "lodash.debounce";
|
|
44
|
-
import {
|
|
44
|
+
import {
|
|
45
|
+
convertTokensToTextNodes,
|
|
46
|
+
generateTokenInstance,
|
|
47
|
+
} from "./components/tiptap/token/Token.js";
|
|
45
48
|
import Fuse from "fuse.js";
|
|
46
49
|
|
|
47
50
|
function EditableTextField({
|
|
@@ -87,17 +90,21 @@ function EditableTextField({
|
|
|
87
90
|
return new Fuse(tokens, { keys: ["label"] });
|
|
88
91
|
}, [tokens]);
|
|
89
92
|
|
|
93
|
+
const tokenOptions = {
|
|
94
|
+
tokens,
|
|
95
|
+
mode,
|
|
96
|
+
isEditable,
|
|
97
|
+
usePlaceholder: isReallyPresoManager,
|
|
98
|
+
...tokenConfig,
|
|
99
|
+
};
|
|
100
|
+
|
|
90
101
|
const baseExtensions = [
|
|
91
102
|
Text,
|
|
92
103
|
Paragraph,
|
|
93
104
|
HardBreak,
|
|
94
105
|
generateTokenInstance({
|
|
95
|
-
|
|
96
|
-
mode,
|
|
97
|
-
isEditable,
|
|
98
|
-
usePlaceholder: isReallyPresoManager,
|
|
106
|
+
...tokenOptions,
|
|
99
107
|
fuse: fuseInstance,
|
|
100
|
-
...tokenConfig,
|
|
101
108
|
}),
|
|
102
109
|
].filter(Boolean);
|
|
103
110
|
|
|
@@ -141,6 +148,12 @@ function EditableTextField({
|
|
|
141
148
|
],
|
|
142
149
|
editable: isEditable,
|
|
143
150
|
autofocus: false,
|
|
151
|
+
onCreate: ({ editor }) => {
|
|
152
|
+
// Burst any saved tokens (eg. added in PresoManager, editable in present)
|
|
153
|
+
if (tokenOptions.burstOnCreate) {
|
|
154
|
+
convertTokensToTextNodes(editor, tokenOptions);
|
|
155
|
+
}
|
|
156
|
+
},
|
|
144
157
|
onUpdate: debounce(({ editor }) => {
|
|
145
158
|
onChange?.(editor.getHTML());
|
|
146
159
|
}, 100),
|