@livepreso/react-plugin-textfield 0.1.2 → 0.2.0
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/.rush/temp/shrinkwrap-deps.json +32 -27
- package/CHANGELOG.json +29 -0
- package/CHANGELOG.md +15 -1
- package/components/InsertTokenDialog.js +82 -0
- package/components/InsertTokenDialog.module.scss +166 -0
- package/components/editor-toolbars/EditorMenu.module.scss +1 -0
- package/components/editor-toolbars/SimpleButton.js +34 -0
- package/components/editor-toolbars/SimpleButton.module.scss +36 -0
- package/components/tiptap/token/Token.js +142 -0
- package/components/tiptap/token/Token.module.scss +28 -0
- package/components/tiptap/token/TokenList.js +93 -0
- package/components/tiptap/token/TokenList.module.scss +58 -0
- package/components/tiptap/token/utils.js +92 -0
- package/configs/generate-toolbar-configuration.js +14 -2
- package/configs/generate-toolbar-options.js +4 -2
- package/configs/toolbar-configuration.js +15 -0
- package/constants.js +19 -1
- package/icons/HelpCenter.js +13 -0
- package/icons/MoreVert.js +13 -0
- package/icons/WandStars.js +13 -0
- package/icons/help_center.svg +1 -0
- package/icons/index.js +9 -1
- package/icons/more_vert.svg +1 -0
- package/icons/wand_stars.svg +1 -0
- package/index.js +48 -4
- package/index.module.scss +9 -8
- package/package.json +30 -27
- package/scripts/extract-svg.js +3 -1
- package/variables.scss +6 -0
- package/icons/format_color_text.svg +0 -5
- package/icons/format_color_text_ungrouped.svg +0 -6
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
forwardRef,
|
|
3
|
+
useEffect,
|
|
4
|
+
useImperativeHandle,
|
|
5
|
+
useState,
|
|
6
|
+
} from "react";
|
|
7
|
+
import classNames from "classnames";
|
|
8
|
+
import style from "./TokenList.module.scss";
|
|
9
|
+
import buttonStyle from "../../editor-toolbars/SimpleButton.module.scss";
|
|
10
|
+
import InsertTokenDialog from "../../InsertTokenDialog";
|
|
11
|
+
import { Separator } from "radix-ui";
|
|
12
|
+
|
|
13
|
+
// the UI associated with the suggestion dropdown
|
|
14
|
+
export const TokenList = forwardRef(
|
|
15
|
+
({ items, choices, command }, forwardedRef) => {
|
|
16
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
17
|
+
|
|
18
|
+
const selectItem = (index) => {
|
|
19
|
+
const item = items[index];
|
|
20
|
+
|
|
21
|
+
if (item) {
|
|
22
|
+
command({ id: item.id, label: item.label });
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const upHandler = () => {
|
|
27
|
+
setSelectedIndex((selectedIndex + items.length - 1) % items.length);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const downHandler = () => {
|
|
31
|
+
setSelectedIndex((selectedIndex + 1) % items.length);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const enterHandler = () => {
|
|
35
|
+
selectItem(selectedIndex);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
useEffect(() => setSelectedIndex(0), [items]);
|
|
39
|
+
|
|
40
|
+
useImperativeHandle(forwardedRef, () => ({
|
|
41
|
+
onKeyDown: ({ event }) => {
|
|
42
|
+
if (event.key === "ArrowUp") {
|
|
43
|
+
upHandler();
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (event.key === "ArrowDown") {
|
|
48
|
+
downHandler();
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (event.key === "Enter") {
|
|
53
|
+
enterHandler();
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
},
|
|
58
|
+
}));
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<div
|
|
62
|
+
className={classNames(style.root, "interactive")}
|
|
63
|
+
data-companywide-interactive
|
|
64
|
+
ref={forwardedRef}
|
|
65
|
+
>
|
|
66
|
+
<div className={style.tokenPopup}>
|
|
67
|
+
<div className={style.dropdownMenu}>
|
|
68
|
+
{items.length ? (
|
|
69
|
+
items.map(({ id, label }, index) => (
|
|
70
|
+
<button
|
|
71
|
+
className={classNames(buttonStyle.button, style.suggestion, {
|
|
72
|
+
[style.isSelected]: index === selectedIndex,
|
|
73
|
+
})}
|
|
74
|
+
key={id}
|
|
75
|
+
onClick={() => selectItem(index)}
|
|
76
|
+
>
|
|
77
|
+
{label}
|
|
78
|
+
</button>
|
|
79
|
+
))
|
|
80
|
+
) : (
|
|
81
|
+
<div className={style.item}>No result</div>
|
|
82
|
+
)}
|
|
83
|
+
</div>
|
|
84
|
+
<Separator.Root className={style.separator} />
|
|
85
|
+
<InsertTokenDialog
|
|
86
|
+
tokens={choices}
|
|
87
|
+
onChange={(token) => command(token)}
|
|
88
|
+
/>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
);
|
|
92
|
+
},
|
|
93
|
+
);
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
@import "../../style.module.scss";
|
|
2
|
+
|
|
3
|
+
:global(.sp-presenter-overlay) {
|
|
4
|
+
.root {
|
|
5
|
+
@extend %root;
|
|
6
|
+
|
|
7
|
+
.token-popup {
|
|
8
|
+
z-index: 1000;
|
|
9
|
+
background: var(--white);
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-direction: column;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/* Dropdown menu */
|
|
15
|
+
.dropdown-menu {
|
|
16
|
+
display: flex;
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
overflow: auto;
|
|
19
|
+
position: relative;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.separator {
|
|
23
|
+
@extend %separator-horizontal;
|
|
24
|
+
flex: none;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.button {
|
|
28
|
+
@extend %item-vertical;
|
|
29
|
+
align-items: center;
|
|
30
|
+
background-color: transparent;
|
|
31
|
+
display: flex;
|
|
32
|
+
text-align: left;
|
|
33
|
+
width: 100%;
|
|
34
|
+
border-radius: none;
|
|
35
|
+
|
|
36
|
+
&:first-child {
|
|
37
|
+
padding-top: 8px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
&:last-child {
|
|
41
|
+
padding-bottom: 8px;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.suggestion {
|
|
46
|
+
padding-left: 12px;
|
|
47
|
+
|
|
48
|
+
&:hover,
|
|
49
|
+
&:hover.is-selected {
|
|
50
|
+
background-color: var(--sp-toolbar-hover-color);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
&.is-selected {
|
|
54
|
+
background-color: var(--sp-toolbar-hover-color);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// modified from http://github.com/ueberdosis/tiptap/blob/main/demos/src/Nodes/Mention/React/suggestion.js
|
|
2
|
+
// the output of the function is a configuration object for the [Suggestion engine](https://tiptap.dev/docs/editor/api/utilities/suggestion)
|
|
3
|
+
// that Tiptap uses to drive the mention / Token plugin's output
|
|
4
|
+
|
|
5
|
+
import { computePosition, flip, shift } from "@floating-ui/react-dom";
|
|
6
|
+
import { posToDOMRect, ReactRenderer } from "@tiptap/react";
|
|
7
|
+
import { TokenList } from "./TokenList";
|
|
8
|
+
|
|
9
|
+
export const TOKEN_TRIGGER_CHAR = "@";
|
|
10
|
+
|
|
11
|
+
const updatePosition = (editor, element) => {
|
|
12
|
+
const virtualElement = {
|
|
13
|
+
getBoundingClientRect: () =>
|
|
14
|
+
posToDOMRect(
|
|
15
|
+
editor.view,
|
|
16
|
+
editor.state.selection.from,
|
|
17
|
+
editor.state.selection.to,
|
|
18
|
+
),
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
computePosition(virtualElement, element, {
|
|
22
|
+
placement: "bottom-start",
|
|
23
|
+
strategy: "absolute",
|
|
24
|
+
middleware: [shift(), flip()],
|
|
25
|
+
}).then(({ x, y, strategy }) => {
|
|
26
|
+
element.style.width = "max-content";
|
|
27
|
+
element.style.position = strategy;
|
|
28
|
+
element.style.left = `${x}px`;
|
|
29
|
+
element.style.top = `${y}px`;
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// TODO wrap this in a useMemo hook or similar to avoid unnecessary instantiations of Fuse
|
|
34
|
+
export function generateSuggestionConfiguration({ tokens = [], fuse }) {
|
|
35
|
+
return {
|
|
36
|
+
char: TOKEN_TRIGGER_CHAR,
|
|
37
|
+
items: ({ query }) => {
|
|
38
|
+
if (query === "") return tokens.slice(0, 5);
|
|
39
|
+
return fuse.search(query, { limit: 5 }).map((r) => r.item);
|
|
40
|
+
},
|
|
41
|
+
// this is a cached value for use in
|
|
42
|
+
choices: tokens,
|
|
43
|
+
// Disable UI if editor is not in editable mode or another condition is met
|
|
44
|
+
allow: ({ editor, state, range }) => editor.isEditable && tokens.length > 0,
|
|
45
|
+
render: () => {
|
|
46
|
+
let component;
|
|
47
|
+
return {
|
|
48
|
+
onStart: (props) => {
|
|
49
|
+
component = new ReactRenderer(TokenList, {
|
|
50
|
+
props: { ...props, choices: tokens },
|
|
51
|
+
editor: props.editor,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
if (!props.clientRect) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
component.element.style.position = "absolute";
|
|
59
|
+
|
|
60
|
+
window.Bridge.UI.overlay.appendChild(component.element);
|
|
61
|
+
|
|
62
|
+
updatePosition(props.editor, component.element);
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
onUpdate(props) {
|
|
66
|
+
component.updateProps(props);
|
|
67
|
+
|
|
68
|
+
if (!props.clientRect) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
updatePosition(props.editor, component.element);
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
onKeyDown(props) {
|
|
76
|
+
if (props.event.key === "Escape") {
|
|
77
|
+
component.destroy();
|
|
78
|
+
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return component.ref?.onKeyDown(props);
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
onExit() {
|
|
86
|
+
component.element.remove();
|
|
87
|
+
component.destroy();
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -4,13 +4,23 @@ import {
|
|
|
4
4
|
TABLE_TOOLBAR_IDS,
|
|
5
5
|
} from "../constants";
|
|
6
6
|
|
|
7
|
-
const generateGroup = (
|
|
7
|
+
const generateGroup = (
|
|
8
|
+
id,
|
|
9
|
+
{ label = null, items: itemsConfig = [], options = {} } = {},
|
|
10
|
+
) => {
|
|
11
|
+
// a given generator can return null
|
|
12
|
+
// if the item will not funciton with the supplied options
|
|
13
|
+
const items = itemsConfig
|
|
14
|
+
.map((generator) => generator(options))
|
|
15
|
+
.filter(Boolean);
|
|
16
|
+
|
|
17
|
+
// if, having run the generators, the group has no items, return null
|
|
8
18
|
if (items.length === 0) return null;
|
|
9
19
|
|
|
10
20
|
return {
|
|
11
21
|
id,
|
|
12
22
|
label,
|
|
13
|
-
items
|
|
23
|
+
items,
|
|
14
24
|
};
|
|
15
25
|
};
|
|
16
26
|
|
|
@@ -75,10 +85,12 @@ export const generateToolbarConfiguration = ({
|
|
|
75
85
|
itemGenerators = {},
|
|
76
86
|
textStyles = [],
|
|
77
87
|
colors = [],
|
|
88
|
+
tokens = [],
|
|
78
89
|
} = {}) => {
|
|
79
90
|
const options = {
|
|
80
91
|
textStyles,
|
|
81
92
|
colors,
|
|
93
|
+
tokens,
|
|
82
94
|
};
|
|
83
95
|
|
|
84
96
|
const generateItem = (item) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { tableItemGenerators } from "./table-toolbar-configuration";
|
|
2
2
|
import { generateToolbarConfiguration } from "./generate-toolbar-configuration";
|
|
3
|
-
import { TABLE, TOOLBAR_SCOPES } from "../constants";
|
|
3
|
+
import { TABLE, TOKEN, TOOLBAR_SCOPES } from "../constants";
|
|
4
4
|
import { primaryItemGenerators } from "./toolbar-configuration";
|
|
5
5
|
|
|
6
6
|
const testUniqueIds = (options) => {
|
|
@@ -26,9 +26,11 @@ export const generateToolbarOptions = ({
|
|
|
26
26
|
tableToolbarConfig = [],
|
|
27
27
|
textStyles = [],
|
|
28
28
|
colors = [],
|
|
29
|
+
tokens = [],
|
|
29
30
|
} = {}) => {
|
|
30
31
|
const includeTableEditor = toolbarConfig.includes(TABLE);
|
|
31
|
-
const
|
|
32
|
+
const includeToken = toolbarConfig.includes(TOKEN) && tokens.length > 0;
|
|
33
|
+
const options = { textStyles, colors, tokens: includeToken ? tokens : [] };
|
|
32
34
|
|
|
33
35
|
const primaryOptions = generateToolbarConfiguration({
|
|
34
36
|
toolbar: toolbarConfig,
|
|
@@ -23,6 +23,7 @@ import LinkEditDialog from "../components/LinkEditDialog";
|
|
|
23
23
|
import { Gapcursor } from "@tiptap/extensions";
|
|
24
24
|
import { ColorPickerCombo } from "../components/color-picker/ColorPickerCombo";
|
|
25
25
|
import { TableCreatorCombo } from "../components/table-creator/TableCreatorCombo";
|
|
26
|
+
import { TOKEN_TRIGGER_CHAR } from "../components/tiptap/token/utils";
|
|
26
27
|
|
|
27
28
|
function generateTextStyleTest(textStyles) {
|
|
28
29
|
return (editor) => {
|
|
@@ -283,6 +284,19 @@ const removeLinkButton = () => ({
|
|
|
283
284
|
enabled: (editor) => editor.isActive("link"),
|
|
284
285
|
icon: <icons.LinkOff />,
|
|
285
286
|
});
|
|
287
|
+
const insertTokenButton = ({ tokens = [] }) =>
|
|
288
|
+
tokens.length
|
|
289
|
+
? {
|
|
290
|
+
id: PRIMARY_TOOLBAR_IDS.INSERT_TOKEN,
|
|
291
|
+
label: "Insert token",
|
|
292
|
+
type: TOOLBAR_COMPONENT_TYPES.BUTTON,
|
|
293
|
+
icon: <icons.WandStars />,
|
|
294
|
+
// Token extension is always enabled, so nothing to load here
|
|
295
|
+
extensions: [],
|
|
296
|
+
command: (editor) =>
|
|
297
|
+
editor.chain().focus().insertContent(TOKEN_TRIGGER_CHAR).run(),
|
|
298
|
+
}
|
|
299
|
+
: null;
|
|
286
300
|
|
|
287
301
|
const clearFormattingButton = () => ({
|
|
288
302
|
id: PRIMARY_TOOLBAR_IDS.CLEAR_FORMATTING,
|
|
@@ -333,6 +347,7 @@ export const primaryItemGenerators = {
|
|
|
333
347
|
[PRIMARY_TOOLBAR_IDS.ORDERED_LIST]: numberedListButton,
|
|
334
348
|
[PRIMARY_TOOLBAR_IDS.INSERT_LINK]: addLinkButton,
|
|
335
349
|
[PRIMARY_TOOLBAR_IDS.REMOVE_LINK]: removeLinkButton,
|
|
350
|
+
[PRIMARY_TOOLBAR_IDS.INSERT_TOKEN]: insertTokenButton,
|
|
336
351
|
[PRIMARY_TOOLBAR_IDS.CLEAR_FORMATTING]: clearFormattingButton,
|
|
337
352
|
[PRIMARY_TOOLBAR_IDS.INSERT_TABLE]: insertTableButton,
|
|
338
353
|
};
|
package/constants.js
CHANGED
|
@@ -30,6 +30,7 @@ export const BLOCK = "block";
|
|
|
30
30
|
export const TEXT_STYLE = "text-style";
|
|
31
31
|
export const ALIGNMENT = "alignment";
|
|
32
32
|
export const LINK = "link";
|
|
33
|
+
export const TOKEN = "token";
|
|
33
34
|
export const LIST = "list";
|
|
34
35
|
export const CLEAR_FORMATTING = "clear-formatting";
|
|
35
36
|
export const TABLE = "table";
|
|
@@ -48,6 +49,7 @@ export const PRIMARY_TOOLBAR_IDS = Object.freeze({
|
|
|
48
49
|
TEXT_ALIGN: "textAlign",
|
|
49
50
|
INSERT_LINK: "insertLink",
|
|
50
51
|
REMOVE_LINK: "unlink",
|
|
52
|
+
INSERT_TOKEN: "insertToken",
|
|
51
53
|
ORDERED_LIST: "orderedList",
|
|
52
54
|
UNORDERED_LIST: "unorderedList",
|
|
53
55
|
INSERT_TABLE: "insertTable",
|
|
@@ -107,6 +109,10 @@ const PRIMARY_TOOLBAR_GROUPS = Object.freeze({
|
|
|
107
109
|
label: "Links",
|
|
108
110
|
items: [PRIMARY_TOOLBAR_IDS.INSERT_LINK, PRIMARY_TOOLBAR_IDS.REMOVE_LINK],
|
|
109
111
|
},
|
|
112
|
+
[TOKEN]: {
|
|
113
|
+
label: "Tokens",
|
|
114
|
+
items: [PRIMARY_TOOLBAR_IDS.INSERT_TOKEN],
|
|
115
|
+
},
|
|
110
116
|
[TABLE]: { label: "Insert table", items: [PRIMARY_TOOLBAR_IDS.INSERT_TABLE] },
|
|
111
117
|
[CLEAR_FORMATTING]: {
|
|
112
118
|
label: "Clear formatting",
|
|
@@ -170,6 +176,7 @@ export const COMPLETE_TOOLBAR_CONFIG = [
|
|
|
170
176
|
TEXT_STYLE,
|
|
171
177
|
ALIGNMENT,
|
|
172
178
|
LINK,
|
|
179
|
+
TOKEN,
|
|
173
180
|
TABLE,
|
|
174
181
|
CLEAR_FORMATTING,
|
|
175
182
|
];
|
|
@@ -185,7 +192,12 @@ export const DEFAULT_TABLE_TOOLBAR_CONFIG = [...COMPLETE_TABLE_TOOLBAR_CONFIG];
|
|
|
185
192
|
|
|
186
193
|
// Alternative toolbar configurations
|
|
187
194
|
export const MINIMAL_TOOLBAR_CONFIG = [UNDO_REDO];
|
|
188
|
-
export const SIMPLE_TOOLBAR_CONFIG = [
|
|
195
|
+
export const SIMPLE_TOOLBAR_CONFIG = [
|
|
196
|
+
UNDO_REDO,
|
|
197
|
+
TEXT_STYLE,
|
|
198
|
+
CLEAR_FORMATTING,
|
|
199
|
+
TOKEN,
|
|
200
|
+
];
|
|
189
201
|
|
|
190
202
|
export const SIMPLE_TABLE_TOOLBAR_CONFIG = [
|
|
191
203
|
DELETE_TABLE,
|
|
@@ -196,3 +208,9 @@ export const SIMPLE_TABLE_TOOLBAR_CONFIG = [
|
|
|
196
208
|
TABLE_TOOLBAR_IDS.REMOVE_ROW,
|
|
197
209
|
],
|
|
198
210
|
];
|
|
211
|
+
|
|
212
|
+
// these are passed to the editable so it knows what mode it is in -
|
|
213
|
+
// useful for plugins that behave differently in different modes
|
|
214
|
+
export const MODE_CMS = "cms";
|
|
215
|
+
export const MODE_PREP = "prep";
|
|
216
|
+
export const MODE_UNMANAGED = "unmanaged";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
const HelpCenter = (props) => (
|
|
3
|
+
<svg
|
|
4
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
5
|
+
height="24px"
|
|
6
|
+
viewBox="0 -960 960 960"
|
|
7
|
+
width="24px"
|
|
8
|
+
{...props}
|
|
9
|
+
>
|
|
10
|
+
<path d="M480.77-257.31q17.15 0 28.96-11.81 11.81-11.8 11.81-28.96 0-17.15-11.81-28.96-11.81-11.81-28.96-11.81t-28.96 11.81Q440-315.23 440-298.08q0 17.16 11.81 28.96 11.81 11.81 28.96 11.81Zm-28.31-143.23h56.31q.77-31.38 8.96-47.81 8.19-16.42 33.04-41.27 31.92-31.92 45.65-54.26 13.73-22.35 13.73-50.81 0-49.92-34.07-80.5-34.08-30.58-87.77-30.58-48.85 0-83.5 24.89-34.66 24.88-49.27 64.8l51.38 20.62q8.54-25.46 28.39-41.58 19.84-16.11 49.77-16.11 32.38 0 50.57 17.57 18.2 17.58 18.2 42.73 0 18.54-11 36.39t-33.93 38.54q-32.61 29.38-44.53 54.54-11.93 25.15-11.93 62.84ZM140-140v-680h680v680H140Zm60-60h560v-560H200v560Zm0 0v-560 560Z" />
|
|
11
|
+
</svg>
|
|
12
|
+
);
|
|
13
|
+
export default HelpCenter;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
const MoreVert = (props) => (
|
|
3
|
+
<svg
|
|
4
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
5
|
+
height="24px"
|
|
6
|
+
viewBox="0 -960 960 960"
|
|
7
|
+
width="24px"
|
|
8
|
+
{...props}
|
|
9
|
+
>
|
|
10
|
+
<path d="M480-189.23q-24.75 0-42.37-17.63Q420-224.48 420-249.23q0-24.75 17.63-42.38 17.62-17.62 42.37-17.62 24.75 0 42.37 17.62Q540-273.98 540-249.23q0 24.75-17.63 42.37-17.62 17.63-42.37 17.63ZM480-420q-24.75 0-42.37-17.63Q420-455.25 420-480q0-24.75 17.63-42.37Q455.25-540 480-540q24.75 0 42.37 17.63Q540-504.75 540-480q0 24.75-17.63 42.37Q504.75-420 480-420Zm0-230.77q-24.75 0-42.37-17.62Q420-686.02 420-710.77q0-24.75 17.63-42.37 17.62-17.63 42.37-17.63 24.75 0 42.37 17.63Q540-735.52 540-710.77q0 24.75-17.63 42.38-17.62 17.62-42.37 17.62Z" />
|
|
11
|
+
</svg>
|
|
12
|
+
);
|
|
13
|
+
export default MoreVert;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
const WandStars = (props) => (
|
|
3
|
+
<svg
|
|
4
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
5
|
+
height="24px"
|
|
6
|
+
viewBox="0 -960 960 960"
|
|
7
|
+
width="24px"
|
|
8
|
+
{...props}
|
|
9
|
+
>
|
|
10
|
+
<path d="M182.15-140 140-182.15 445.23-488l-174.84-43.08L453-644.46 437.16-860l164.76 139.08L801-802l-80.08 199.69L860-438.16 644.46-453l-114 182.61L487-445.23 182.15-140Zm23.62-523.85L140-729.62l65.77-65.76 65.77 65.76-65.77 65.77ZM555-422.61l57.23-93.24 109.54 8.16L651-591.77l41.16-101.39L590.77-652l-84.08-70.16 8.16 108.93L422-555.38l106.15 26.23L555-422.61ZM729.62-140l-65.77-65.77 65.77-65.77 65.76 65.77L729.62-140ZM572.08-572.69Z" />
|
|
11
|
+
</svg>
|
|
12
|
+
);
|
|
13
|
+
export default WandStars;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M480.77-257.31q17.15 0 28.96-11.81 11.81-11.8 11.81-28.96 0-17.15-11.81-28.96-11.81-11.81-28.96-11.81t-28.96 11.81Q440-315.23 440-298.08q0 17.16 11.81 28.96 11.81 11.81 28.96 11.81Zm-28.31-143.23h56.31q.77-31.38 8.96-47.81 8.19-16.42 33.04-41.27 31.92-31.92 45.65-54.26 13.73-22.35 13.73-50.81 0-49.92-34.07-80.5-34.08-30.58-87.77-30.58-48.85 0-83.5 24.89-34.66 24.88-49.27 64.8l51.38 20.62q8.54-25.46 28.39-41.58 19.84-16.11 49.77-16.11 32.38 0 50.57 17.57 18.2 17.58 18.2 42.73 0 18.54-11 36.39t-33.93 38.54q-32.61 29.38-44.53 54.54-11.93 25.15-11.93 62.84ZM140-140v-680h680v680H140Zm60-60h560v-560H200v560Zm0 0v-560 560Z"/></svg>
|
package/icons/index.js
CHANGED
|
@@ -41,12 +41,14 @@ import FormatListBulleted from "./FormatListBulleted.js";
|
|
|
41
41
|
import FormatListNumbered from "./FormatListNumbered.js";
|
|
42
42
|
import FormatStrikethrough from "./FormatStrikethrough.js";
|
|
43
43
|
import FormatUnderlined from "./FormatUnderlined.js";
|
|
44
|
+
import HelpCenter from "./HelpCenter.js";
|
|
44
45
|
import HorizontalRule from "./HorizontalRule.js";
|
|
45
46
|
import Italic from "./Italic.js";
|
|
46
47
|
import ItalicIcon from "./ItalicIcon.js";
|
|
47
48
|
import Link from "./Link.js";
|
|
48
49
|
import LinkOff from "./LinkOff.js";
|
|
49
50
|
import MergeCells from "./MergeCells.js";
|
|
51
|
+
import MoreVert from "./MoreVert.js";
|
|
50
52
|
import Redo from "./Redo.js";
|
|
51
53
|
import RemoveColumnOutline from "./RemoveColumnOutline.js";
|
|
52
54
|
import RemoveRowOutline from "./RemoveRowOutline.js";
|
|
@@ -59,6 +61,7 @@ import Undo from "./Undo.js";
|
|
|
59
61
|
import VerticalAlignBottom from "./VerticalAlignBottom.js";
|
|
60
62
|
import VerticalAlignCenter from "./VerticalAlignCenter.js";
|
|
61
63
|
import VerticalAlignTop from "./VerticalAlignTop.js";
|
|
64
|
+
import WandStars from "./WandStars.js";
|
|
62
65
|
|
|
63
66
|
export const MATERIAL_SVG_PARAMS = {
|
|
64
67
|
style: "sharp",
|
|
@@ -101,19 +104,20 @@ export {
|
|
|
101
104
|
FormatBold,
|
|
102
105
|
FormatClear,
|
|
103
106
|
FormatColorFill,
|
|
104
|
-
FormatColorText,
|
|
105
107
|
FormatItalic,
|
|
106
108
|
FormatLineSpacing,
|
|
107
109
|
FormatListBulleted,
|
|
108
110
|
FormatListNumbered,
|
|
109
111
|
FormatStrikethrough,
|
|
110
112
|
FormatUnderlined,
|
|
113
|
+
HelpCenter,
|
|
111
114
|
HorizontalRule,
|
|
112
115
|
Italic,
|
|
113
116
|
ItalicIcon,
|
|
114
117
|
Link,
|
|
115
118
|
LinkOff,
|
|
116
119
|
MergeCells,
|
|
120
|
+
MoreVert,
|
|
117
121
|
Redo,
|
|
118
122
|
RemoveColumnOutline,
|
|
119
123
|
RemoveRowOutline,
|
|
@@ -126,6 +130,7 @@ export {
|
|
|
126
130
|
VerticalAlignBottom,
|
|
127
131
|
VerticalAlignCenter,
|
|
128
132
|
VerticalAlignTop,
|
|
133
|
+
WandStars,
|
|
129
134
|
};
|
|
130
135
|
|
|
131
136
|
export default {
|
|
@@ -170,12 +175,14 @@ export default {
|
|
|
170
175
|
FormatListNumbered,
|
|
171
176
|
FormatStrikethrough,
|
|
172
177
|
FormatUnderlined,
|
|
178
|
+
HelpCenter,
|
|
173
179
|
HorizontalRule,
|
|
174
180
|
Italic,
|
|
175
181
|
ItalicIcon,
|
|
176
182
|
Link,
|
|
177
183
|
LinkOff,
|
|
178
184
|
MergeCells,
|
|
185
|
+
MoreVert,
|
|
179
186
|
Redo,
|
|
180
187
|
RemoveColumnOutline,
|
|
181
188
|
RemoveRowOutline,
|
|
@@ -188,4 +195,5 @@ export default {
|
|
|
188
195
|
VerticalAlignBottom,
|
|
189
196
|
VerticalAlignCenter,
|
|
190
197
|
VerticalAlignTop,
|
|
198
|
+
WandStars,
|
|
191
199
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M480-189.23q-24.75 0-42.37-17.63Q420-224.48 420-249.23q0-24.75 17.63-42.38 17.62-17.62 42.37-17.62 24.75 0 42.37 17.62Q540-273.98 540-249.23q0 24.75-17.63 42.37-17.62 17.63-42.37 17.63ZM480-420q-24.75 0-42.37-17.63Q420-455.25 420-480q0-24.75 17.63-42.37Q455.25-540 480-540q24.75 0 42.37 17.63Q540-504.75 540-480q0 24.75-17.63 42.37Q504.75-420 480-420Zm0-230.77q-24.75 0-42.37-17.62Q420-686.02 420-710.77q0-24.75 17.63-42.37 17.62-17.63 42.37-17.63 24.75 0 42.37 17.63Q540-735.52 540-710.77q0 24.75-17.63 42.38-17.62 17.62-42.37 17.62Z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M182.15-140 140-182.15 445.23-488l-174.84-43.08L453-644.46 437.16-860l164.76 139.08L801-802l-80.08 199.69L860-438.16 644.46-453l-114 182.61L487-445.23 182.15-140Zm23.62-523.85L140-729.62l65.77-65.76 65.77 65.76-65.77 65.77ZM555-422.61l57.23-93.24 109.54 8.16L651-591.77l41.16-101.39L590.77-652l-84.08-70.16 8.16 108.93L422-555.38l106.15 26.23L555-422.61ZM729.62-140l-65.77-65.77 65.77-65.77 65.76 65.77L729.62-140ZM572.08-572.69Z"/></svg>
|
package/index.js
CHANGED
|
@@ -33,11 +33,16 @@ import {
|
|
|
33
33
|
DEFAULT_TOOLBAR_CONFIG,
|
|
34
34
|
DEFAULT_TABLE_TOOLBAR_CONFIG,
|
|
35
35
|
EDITABLE_OUTER_SELECTOR,
|
|
36
|
+
MODE_PREP,
|
|
37
|
+
MODE_CMS,
|
|
38
|
+
MODE_UNMANAGED,
|
|
36
39
|
} from "./constants.js";
|
|
37
40
|
import { TableCellMenu } from "./components/TableCellMenu.js";
|
|
38
41
|
import style from "./index.module.scss";
|
|
39
42
|
import { generateExtensionsFromTag } from "./utils/generateCustomExtensions.js";
|
|
40
43
|
import debounce from "lodash.debounce";
|
|
44
|
+
import { generateTokenInstance } from "./components/tiptap/token/Token.js";
|
|
45
|
+
import Fuse from "fuse.js";
|
|
41
46
|
|
|
42
47
|
function EditableTextField({
|
|
43
48
|
value,
|
|
@@ -46,6 +51,7 @@ function EditableTextField({
|
|
|
46
51
|
isEditable,
|
|
47
52
|
textStyles,
|
|
48
53
|
colors,
|
|
54
|
+
tokens,
|
|
49
55
|
toolbarConfig = DEFAULT_TOOLBAR_CONFIG,
|
|
50
56
|
tableToolbarConfig = DEFAULT_TABLE_TOOLBAR_CONFIG,
|
|
51
57
|
maskSelector,
|
|
@@ -53,14 +59,20 @@ function EditableTextField({
|
|
|
53
59
|
tag,
|
|
54
60
|
autofocus = false,
|
|
55
61
|
disableSpellcheck = false,
|
|
62
|
+
mode,
|
|
63
|
+
tokenConfig = {},
|
|
56
64
|
// TODO implement an equivalent to the EditableText handler
|
|
57
65
|
// for editables inside tab handlers / accordion headers etc
|
|
58
66
|
// stopPropagation = false,
|
|
59
67
|
...other
|
|
60
68
|
}) {
|
|
69
|
+
const { isPresomanager } = useModes();
|
|
70
|
+
const { isUserTemplateLibrary } = useSlide();
|
|
61
71
|
const [textColor, setTextColor] = useState(null);
|
|
62
72
|
const initialValue = useRef(value);
|
|
63
73
|
|
|
74
|
+
const isReallyPresoManager = isPresomanager && !isUserTemplateLibrary;
|
|
75
|
+
|
|
64
76
|
// a custom tag can result in a custom Document extension
|
|
65
77
|
// or you can pass custom extensions
|
|
66
78
|
const additionalExtensions = {
|
|
@@ -68,7 +80,26 @@ function EditableTextField({
|
|
|
68
80
|
...customExtensions,
|
|
69
81
|
};
|
|
70
82
|
|
|
71
|
-
|
|
83
|
+
// include Token here, because we need it for rendering even if its toolbar button is disabled
|
|
84
|
+
// elsewhere, we can kill its suggestion engine to disable its UI
|
|
85
|
+
|
|
86
|
+
const fuseInstance = useMemo(() => {
|
|
87
|
+
return new Fuse(tokens, { keys: ["label"] });
|
|
88
|
+
}, [tokens]);
|
|
89
|
+
|
|
90
|
+
const baseExtensions = [
|
|
91
|
+
Text,
|
|
92
|
+
Paragraph,
|
|
93
|
+
HardBreak,
|
|
94
|
+
generateTokenInstance({
|
|
95
|
+
tokens,
|
|
96
|
+
mode,
|
|
97
|
+
isEditable,
|
|
98
|
+
usePlaceholder: isReallyPresoManager,
|
|
99
|
+
fuse: fuseInstance,
|
|
100
|
+
...tokenConfig,
|
|
101
|
+
}),
|
|
102
|
+
].filter(Boolean);
|
|
72
103
|
|
|
73
104
|
// it is your responsibility not to double-specify any extensions you need here
|
|
74
105
|
// vs those derived from the toolbar config
|
|
@@ -89,8 +120,9 @@ function EditableTextField({
|
|
|
89
120
|
tableToolbarConfig,
|
|
90
121
|
textStyles,
|
|
91
122
|
colors,
|
|
123
|
+
tokens,
|
|
92
124
|
}),
|
|
93
|
-
[textStyles, colors, toolbarConfig, tableToolbarConfig],
|
|
125
|
+
[textStyles, colors, tokens, toolbarConfig, tableToolbarConfig],
|
|
94
126
|
);
|
|
95
127
|
|
|
96
128
|
const groupedOptions = [
|
|
@@ -112,6 +144,7 @@ function EditableTextField({
|
|
|
112
144
|
onUpdate: debounce(({ editor }) => {
|
|
113
145
|
onChange?.(editor.getHTML());
|
|
114
146
|
}, 100),
|
|
147
|
+
parseOptions: { preserveWhitespace: "full" },
|
|
115
148
|
});
|
|
116
149
|
|
|
117
150
|
const { refs } = useFloating({
|
|
@@ -219,8 +252,16 @@ function EditableTextField({
|
|
|
219
252
|
{isEditable && (
|
|
220
253
|
<>
|
|
221
254
|
<PrimaryToolbar configuration={primaryOptions} {...toolbarOptions} />
|
|
222
|
-
|
|
223
|
-
|
|
255
|
+
|
|
256
|
+
{tableOptions.length > 0 && (
|
|
257
|
+
<>
|
|
258
|
+
<TableCellMenu
|
|
259
|
+
configuration={tableCellOptions}
|
|
260
|
+
{...toolbarOptions}
|
|
261
|
+
/>
|
|
262
|
+
<TableToolbar configuration={tableOptions} {...toolbarOptions} />
|
|
263
|
+
</>
|
|
264
|
+
)}
|
|
224
265
|
</>
|
|
225
266
|
)}
|
|
226
267
|
<div
|
|
@@ -257,6 +298,7 @@ function PrepEditableField({ id, prepId = id, children, isCompany, ...props }) {
|
|
|
257
298
|
value={value ?? renderToString(children)}
|
|
258
299
|
onChange={setPrepValue}
|
|
259
300
|
isEditable={isEditable}
|
|
301
|
+
mode={MODE_PREP}
|
|
260
302
|
/>
|
|
261
303
|
);
|
|
262
304
|
}
|
|
@@ -286,6 +328,7 @@ function CMSValField({ id, children, ...props }) {
|
|
|
286
328
|
value={value ?? renderToString(children)}
|
|
287
329
|
onChange={setCMSValue}
|
|
288
330
|
isEditable={isEditable}
|
|
331
|
+
mode={MODE_CMS}
|
|
289
332
|
/>
|
|
290
333
|
);
|
|
291
334
|
}
|
|
@@ -298,6 +341,7 @@ export function TextField(props) {
|
|
|
298
341
|
value={value ?? renderToString(children)}
|
|
299
342
|
onChange={onChange}
|
|
300
343
|
isEditable={!isReadOnly}
|
|
344
|
+
mode={MODE_UNMANAGED}
|
|
301
345
|
{...other}
|
|
302
346
|
/>
|
|
303
347
|
);
|