@lowdefy/blocks-tiptap 0.0.0-experimental-20260428103147
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/LICENSE +201 -0
- package/README.md +1 -0
- package/dist/blocks/TiptapInput/TiptapInput.js +170 -0
- package/dist/blocks/TiptapInput/e2e.js +25 -0
- package/dist/blocks/TiptapInput/meta.js +212 -0
- package/dist/blocks/TiptapInput/style.module.css +60 -0
- package/dist/blocks/TiptapInput/useTiptapState.js +104 -0
- package/dist/blocks/TiptapMentionInput/MentionList.js +69 -0
- package/dist/blocks/TiptapMentionInput/TiptapMentionInput.js +238 -0
- package/dist/blocks/TiptapMentionInput/e2e.js +25 -0
- package/dist/blocks/TiptapMentionInput/meta.js +231 -0
- package/dist/blocks/TiptapMentionInput/style.module.css +96 -0
- package/dist/blocks/TiptapMentionInput/suggestion.js +82 -0
- package/dist/blocks/TiptapMentionInput/useTiptapMentionState.js +108 -0
- package/dist/blocks/utils/PopoverMenu.js +82 -0
- package/dist/blocks/utils/buildExtensions.js +127 -0
- package/dist/blocks/utils/computeHeightStyle.js +57 -0
- package/dist/blocks/utils/s3FileUpload.js +51 -0
- package/dist/blocks/utils/statusClass.js +24 -0
- package/dist/blocks/utils/style.module.css +128 -0
- package/dist/blocks.js +16 -0
- package/dist/e2e.js +16 -0
- package/dist/metas.js +16 -0
- package/dist/types.js +17 -0
- package/package.json +87 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/ import { useRef } from 'react';
|
|
16
|
+
import TurndownService from 'turndown';
|
|
17
|
+
import s3FileUpload from '../utils/s3FileUpload.js';
|
|
18
|
+
// Ref-tracked controller for the editor's value. No useState — the only
|
|
19
|
+
// source of truth for `fileList` is the `value` prop from Lowdefy. We mirror
|
|
20
|
+
// it into a ref each render so callbacks (onUpdate, insertImage) captured in
|
|
21
|
+
// closures always read the current value instead of a stale render snapshot.
|
|
22
|
+
function useTiptapState({ value, methods }) {
|
|
23
|
+
const valueRef = useRef(value);
|
|
24
|
+
valueRef.current = value;
|
|
25
|
+
const turndownService = new TurndownService();
|
|
26
|
+
turndownService.addRule('encodeImgUrl', {
|
|
27
|
+
filter: 'img',
|
|
28
|
+
replacement: (content, node)=>{
|
|
29
|
+
const src = node.getAttribute('src');
|
|
30
|
+
return `})`;
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
// Emit a full `{fileList, html, text, markdown}` value to Lowdefy based on
|
|
34
|
+
// the editor's current document. When appendFile is supplied (an S3 upload
|
|
35
|
+
// result), it is added to the current fileList before filtering by
|
|
36
|
+
// in-document image urls.
|
|
37
|
+
const emit = (editor, appendFile)=>{
|
|
38
|
+
const html = editor.getHTML();
|
|
39
|
+
const markdown = turndownService.turndown(html);
|
|
40
|
+
const json = editor.getJSON();
|
|
41
|
+
const text = editor.getText().trim() === '' ? null : editor.getText();
|
|
42
|
+
const urls = (json.content ?? []).filter((c)=>c.type === 'image').map((c)=>c.attrs.src);
|
|
43
|
+
const base = valueRef.current?.fileList ?? [];
|
|
44
|
+
const next = appendFile ? [
|
|
45
|
+
...base,
|
|
46
|
+
appendFile
|
|
47
|
+
] : base;
|
|
48
|
+
const fileList = next.filter((f)=>urls.includes(f.url));
|
|
49
|
+
methods.setValue({
|
|
50
|
+
fileList,
|
|
51
|
+
html,
|
|
52
|
+
text,
|
|
53
|
+
markdown
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
const insertImage = async (editor, file, pos)=>{
|
|
57
|
+
const url = await s3FileUpload({
|
|
58
|
+
file,
|
|
59
|
+
methods
|
|
60
|
+
});
|
|
61
|
+
editor.chain().insertContentAt(pos, [
|
|
62
|
+
{
|
|
63
|
+
type: 'image',
|
|
64
|
+
attrs: {
|
|
65
|
+
src: url
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
type: 'paragraph',
|
|
70
|
+
content: [
|
|
71
|
+
{
|
|
72
|
+
type: 'text',
|
|
73
|
+
marks: [
|
|
74
|
+
{
|
|
75
|
+
type: 'link',
|
|
76
|
+
attrs: {
|
|
77
|
+
href: url,
|
|
78
|
+
target: '_blank'
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
],
|
|
82
|
+
text: 'Enlarge Image'
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
]).focus().run();
|
|
87
|
+
const fileObj = {
|
|
88
|
+
bucket: file.bucket,
|
|
89
|
+
key: file.key,
|
|
90
|
+
lastModified: file.lastModified,
|
|
91
|
+
name: file.name,
|
|
92
|
+
size: file.size,
|
|
93
|
+
status: file.status,
|
|
94
|
+
type: file.type,
|
|
95
|
+
url
|
|
96
|
+
};
|
|
97
|
+
emit(editor, fileObj);
|
|
98
|
+
};
|
|
99
|
+
return {
|
|
100
|
+
emit,
|
|
101
|
+
insertImage
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
export default useTiptapState;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/ import React, { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
|
|
16
|
+
import { renderHtml } from '@lowdefy/block-utils';
|
|
17
|
+
const MentionList = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
18
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
19
|
+
const selectItem = (index)=>{
|
|
20
|
+
const item = props.items[index];
|
|
21
|
+
if (item) {
|
|
22
|
+
props.command({
|
|
23
|
+
id: item
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const upHandler = ()=>{
|
|
28
|
+
setSelectedIndex((selectedIndex + props.items.length - 1) % props.items.length);
|
|
29
|
+
};
|
|
30
|
+
const downHandler = ()=>{
|
|
31
|
+
setSelectedIndex((selectedIndex + 1) % props.items.length);
|
|
32
|
+
};
|
|
33
|
+
const enterHandler = ()=>{
|
|
34
|
+
selectItem(selectedIndex);
|
|
35
|
+
};
|
|
36
|
+
useEffect(()=>setSelectedIndex(0), [
|
|
37
|
+
props.items
|
|
38
|
+
]);
|
|
39
|
+
useImperativeHandle(ref, ()=>({
|
|
40
|
+
onKeyDown: ({ event })=>{
|
|
41
|
+
if (event.key === 'ArrowUp') {
|
|
42
|
+
upHandler();
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
if (event.key === 'ArrowDown') {
|
|
46
|
+
downHandler();
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
if (event.key === 'Enter') {
|
|
50
|
+
enterHandler();
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}));
|
|
56
|
+
return /*#__PURE__*/ React.createElement("div", {
|
|
57
|
+
className: "tiptap-mention-items"
|
|
58
|
+
}, props.items.length ? props.items.map((item, index)=>/*#__PURE__*/ React.createElement("button", {
|
|
59
|
+
className: `tiptap-mention-item ${index === selectedIndex ? 'is-selected' : ''}`,
|
|
60
|
+
key: index,
|
|
61
|
+
onClick: ()=>selectItem(index)
|
|
62
|
+
}, renderHtml({
|
|
63
|
+
html: item?.label ?? item,
|
|
64
|
+
methods: props.methods
|
|
65
|
+
}))) : /*#__PURE__*/ React.createElement("div", {
|
|
66
|
+
className: "tiptap-mention-item secondary"
|
|
67
|
+
}, "No matching results"));
|
|
68
|
+
});
|
|
69
|
+
export default MentionList;
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/ import React, { useEffect } from 'react';
|
|
16
|
+
import { withBlockDefaults } from '@lowdefy/block-utils';
|
|
17
|
+
import { type } from '@lowdefy/helpers';
|
|
18
|
+
import { useEditor, EditorContent } from '@tiptap/react';
|
|
19
|
+
import { mergeAttributes } from '@tiptap/core';
|
|
20
|
+
import Mention from '@tiptap/extension-mention';
|
|
21
|
+
import Label from '@lowdefy/blocks-antd/blocks/Label/Label.js';
|
|
22
|
+
import PopoverMenu from '../utils/PopoverMenu.js';
|
|
23
|
+
import buildExtensions from '../utils/buildExtensions.js';
|
|
24
|
+
import computeHeightStyle from '../utils/computeHeightStyle.js';
|
|
25
|
+
import statusClass from '../utils/statusClass.js';
|
|
26
|
+
import suggestion from './suggestion.js';
|
|
27
|
+
import useTiptapMentionState from './useTiptapMentionState.js';
|
|
28
|
+
import './style.module.css';
|
|
29
|
+
function mentionLabel(id) {
|
|
30
|
+
return id?.tag?.title ?? id?.label ?? id;
|
|
31
|
+
}
|
|
32
|
+
const TiptapMentionInput = ({ blockId, components: { Icon, Link }, events, loading, methods, properties, required, validation, value })=>{
|
|
33
|
+
const { emit, insertImage } = useTiptapMentionState({
|
|
34
|
+
value,
|
|
35
|
+
methods
|
|
36
|
+
});
|
|
37
|
+
const disabled = properties.disabled === true || loading;
|
|
38
|
+
const uploadEnabled = !type.isNone(properties.s3PostPolicyRequestId);
|
|
39
|
+
const char = properties.mentions?.char ?? '@';
|
|
40
|
+
const allowSpaces = properties.mentions?.allowSpaces !== false;
|
|
41
|
+
const mentionExtension = Mention.configure({
|
|
42
|
+
HTMLAttributes: {
|
|
43
|
+
class: 'tiptap-mention'
|
|
44
|
+
},
|
|
45
|
+
renderHTML ({ options, node }) {
|
|
46
|
+
const label = mentionLabel(node.attrs.id);
|
|
47
|
+
if (type.isFunction(properties.mentions?.getHref)) {
|
|
48
|
+
return [
|
|
49
|
+
'a',
|
|
50
|
+
mergeAttributes({
|
|
51
|
+
href: properties.mentions.getHref(node.attrs.id),
|
|
52
|
+
'data-id': node.attrs.id?._id
|
|
53
|
+
}, options.HTMLAttributes),
|
|
54
|
+
`${char}${label}`
|
|
55
|
+
];
|
|
56
|
+
}
|
|
57
|
+
return [
|
|
58
|
+
'span',
|
|
59
|
+
mergeAttributes(options.HTMLAttributes),
|
|
60
|
+
`${char}${label}`
|
|
61
|
+
];
|
|
62
|
+
},
|
|
63
|
+
renderText ({ node }) {
|
|
64
|
+
return `${char}${mentionLabel(node.attrs.id)}`;
|
|
65
|
+
},
|
|
66
|
+
suggestion: suggestion({
|
|
67
|
+
methods,
|
|
68
|
+
char,
|
|
69
|
+
allowSpaces
|
|
70
|
+
})
|
|
71
|
+
});
|
|
72
|
+
const extensions = buildExtensions({
|
|
73
|
+
properties,
|
|
74
|
+
insertImage,
|
|
75
|
+
mentionExtension,
|
|
76
|
+
uploadEnabled
|
|
77
|
+
});
|
|
78
|
+
const heightStyle = computeHeightStyle({
|
|
79
|
+
rows: properties.rows,
|
|
80
|
+
autoSize: properties.autoSize
|
|
81
|
+
});
|
|
82
|
+
const editor = useEditor({
|
|
83
|
+
editorProps: {
|
|
84
|
+
items: type.isArray(properties.mentions?.options) ? properties.mentions.options : []
|
|
85
|
+
},
|
|
86
|
+
extensions,
|
|
87
|
+
content: value?.html || '',
|
|
88
|
+
editable: ()=>!disabled,
|
|
89
|
+
onUpdate: ({ editor })=>{
|
|
90
|
+
emit(editor);
|
|
91
|
+
methods.triggerEvent({
|
|
92
|
+
name: 'onChange'
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
useEffect(()=>{
|
|
97
|
+
if (editor) {
|
|
98
|
+
editor.setOptions({
|
|
99
|
+
editorProps: {
|
|
100
|
+
items: type.isArray(properties.mentions?.options) ? properties.mentions.options : []
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}, [
|
|
105
|
+
properties.mentions?.options,
|
|
106
|
+
editor
|
|
107
|
+
]);
|
|
108
|
+
useEffect(()=>{
|
|
109
|
+
if (uploadEnabled) {
|
|
110
|
+
methods.registerEvent({
|
|
111
|
+
name: '__getS3PostPolicy',
|
|
112
|
+
actions: [
|
|
113
|
+
{
|
|
114
|
+
id: '__getS3PostPolicy',
|
|
115
|
+
type: 'Request',
|
|
116
|
+
params: [
|
|
117
|
+
properties.s3PostPolicyRequestId
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
]
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
if (!type.isNone(properties.mentionsRequestId)) {
|
|
124
|
+
methods.registerEvent({
|
|
125
|
+
name: '__getTipTapMentions',
|
|
126
|
+
actions: [
|
|
127
|
+
{
|
|
128
|
+
id: '__getTipTapMentions',
|
|
129
|
+
type: 'Request',
|
|
130
|
+
params: [
|
|
131
|
+
properties.mentionsRequestId
|
|
132
|
+
]
|
|
133
|
+
}
|
|
134
|
+
]
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}, []);
|
|
138
|
+
useEffect(()=>{
|
|
139
|
+
if (!editor) return;
|
|
140
|
+
methods.registerMethod('clear', ()=>{
|
|
141
|
+
editor.commands.clearContent();
|
|
142
|
+
emit(editor);
|
|
143
|
+
});
|
|
144
|
+
methods.registerMethod('setContent', (args)=>{
|
|
145
|
+
editor.commands.setContent(args?.html ?? '');
|
|
146
|
+
emit(editor);
|
|
147
|
+
});
|
|
148
|
+
methods.registerMethod('focus', ()=>{
|
|
149
|
+
editor.commands.focus();
|
|
150
|
+
});
|
|
151
|
+
}, [
|
|
152
|
+
editor
|
|
153
|
+
]);
|
|
154
|
+
// External value.html → editor sync. One-way only: we read value.html and
|
|
155
|
+
// push it into the editor with setContent(..., false) so tiptap's onUpdate
|
|
156
|
+
// does not fire. No write-back via emit() — that would race with concurrent
|
|
157
|
+
// SetState calls (child effects fire before parent effects, so a sibling's
|
|
158
|
+
// onMount SetState could be overwritten by our derived emit). Derived
|
|
159
|
+
// fields (text/markdown/fileList/mentions) are populated on user interaction
|
|
160
|
+
// via onUpdate; downstream consumers of seeded content should read
|
|
161
|
+
// value.html directly, or include the fields they need in their SetState
|
|
162
|
+
// payload.
|
|
163
|
+
useEffect(()=>{
|
|
164
|
+
if (!editor) return;
|
|
165
|
+
const next = value?.html ?? '';
|
|
166
|
+
const current = editor.getHTML();
|
|
167
|
+
if (next !== current) {
|
|
168
|
+
editor.commands.setContent(next, false);
|
|
169
|
+
}
|
|
170
|
+
}, [
|
|
171
|
+
value?.html,
|
|
172
|
+
editor
|
|
173
|
+
]);
|
|
174
|
+
useEffect(()=>{
|
|
175
|
+
if (!editor) return;
|
|
176
|
+
editor.setOptions({
|
|
177
|
+
editable: !disabled
|
|
178
|
+
});
|
|
179
|
+
}, [
|
|
180
|
+
editor,
|
|
181
|
+
disabled
|
|
182
|
+
]);
|
|
183
|
+
useEffect(()=>{
|
|
184
|
+
if (!editor) return;
|
|
185
|
+
const placeholderExt = editor.extensionManager.extensions.find((extension)=>extension.name === 'placeholder');
|
|
186
|
+
if (placeholderExt) {
|
|
187
|
+
placeholderExt.options.placeholder = properties.placeholder ?? '';
|
|
188
|
+
editor.view.dispatch(editor.state.tr);
|
|
189
|
+
}
|
|
190
|
+
}, [
|
|
191
|
+
editor,
|
|
192
|
+
properties.placeholder
|
|
193
|
+
]);
|
|
194
|
+
const wrapperClass = [
|
|
195
|
+
'tiptap-wrapper',
|
|
196
|
+
properties.bordered === false ? 'tiptap-wrapper-borderless' : '',
|
|
197
|
+
disabled ? 'tiptap-wrapper-disabled' : '',
|
|
198
|
+
statusClass(validation?.status)
|
|
199
|
+
].filter(Boolean).join(' ');
|
|
200
|
+
const wrapperStyle = {
|
|
201
|
+
padding: 0,
|
|
202
|
+
...heightStyle,
|
|
203
|
+
...properties.inputStyle,
|
|
204
|
+
...properties.style
|
|
205
|
+
};
|
|
206
|
+
return /*#__PURE__*/ React.createElement(Label, {
|
|
207
|
+
blockId: blockId,
|
|
208
|
+
components: {
|
|
209
|
+
Icon,
|
|
210
|
+
Link
|
|
211
|
+
},
|
|
212
|
+
events: events,
|
|
213
|
+
properties: {
|
|
214
|
+
title: properties.title,
|
|
215
|
+
size: properties.size,
|
|
216
|
+
...properties.label
|
|
217
|
+
},
|
|
218
|
+
required: required,
|
|
219
|
+
validation: validation,
|
|
220
|
+
content: {
|
|
221
|
+
content: ()=>{
|
|
222
|
+
if (!editor) {
|
|
223
|
+
return /*#__PURE__*/ React.createElement("div", null);
|
|
224
|
+
}
|
|
225
|
+
return /*#__PURE__*/ React.createElement(React.Fragment, null, /*#__PURE__*/ React.createElement(EditorContent, {
|
|
226
|
+
id: `${blockId}_input`,
|
|
227
|
+
editor: editor,
|
|
228
|
+
className: wrapperClass,
|
|
229
|
+
style: wrapperStyle
|
|
230
|
+
}), !disabled && /*#__PURE__*/ React.createElement(PopoverMenu, {
|
|
231
|
+
editor: editor,
|
|
232
|
+
Icon: Icon
|
|
233
|
+
}));
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
};
|
|
238
|
+
export default withBlockDefaults(TiptapMentionInput);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/ import { createBlockHelper, escapeId } from '@lowdefy/e2e-utils';
|
|
16
|
+
import { expect } from '@playwright/test';
|
|
17
|
+
const locator = (page, blockId)=>page.locator(`#${escapeId(blockId)}_input .ProseMirror`).first();
|
|
18
|
+
export default createBlockHelper({
|
|
19
|
+
locator,
|
|
20
|
+
expect: {
|
|
21
|
+
containsText: (page, blockId, text)=>expect(locator(page, blockId)).toContainText(text),
|
|
22
|
+
containsHtml: (page, blockId, selector)=>expect(locator(page, blockId).locator(selector)).toBeVisible(),
|
|
23
|
+
isEmpty: (page, blockId)=>expect(locator(page, blockId)).toHaveText('')
|
|
24
|
+
}
|
|
25
|
+
});
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/ export default {
|
|
16
|
+
category: 'input',
|
|
17
|
+
icons: [],
|
|
18
|
+
valueType: 'object',
|
|
19
|
+
initValue: {
|
|
20
|
+
html: null,
|
|
21
|
+
text: null,
|
|
22
|
+
markdown: null,
|
|
23
|
+
fileList: [],
|
|
24
|
+
mentions: []
|
|
25
|
+
},
|
|
26
|
+
events: {
|
|
27
|
+
onChange: 'Trigger action when the editor content is changed.'
|
|
28
|
+
},
|
|
29
|
+
properties: {
|
|
30
|
+
type: 'object',
|
|
31
|
+
additionalProperties: false,
|
|
32
|
+
properties: {
|
|
33
|
+
allowedMimeTypes: {
|
|
34
|
+
type: 'array',
|
|
35
|
+
items: {
|
|
36
|
+
type: 'string'
|
|
37
|
+
},
|
|
38
|
+
description: 'Mime-types accepted by the drag/drop and paste file handler. Defaults to common image types. Only used when s3PostPolicyRequestId is set.'
|
|
39
|
+
},
|
|
40
|
+
autoSize: {
|
|
41
|
+
oneOf: [
|
|
42
|
+
{
|
|
43
|
+
type: 'boolean',
|
|
44
|
+
default: false,
|
|
45
|
+
description: 'When true the editor grows with its content and has no max height.'
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
type: 'object',
|
|
49
|
+
description: 'Constrain the editor height with a minimum and/or maximum row count.',
|
|
50
|
+
properties: {
|
|
51
|
+
minRows: {
|
|
52
|
+
type: 'integer',
|
|
53
|
+
minimum: 1
|
|
54
|
+
},
|
|
55
|
+
maxRows: {
|
|
56
|
+
type: 'integer',
|
|
57
|
+
minimum: 1
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
],
|
|
62
|
+
description: 'Either a boolean (true to auto-grow without a cap) or an object with minRows/maxRows. Ignored when `rows` is set.'
|
|
63
|
+
},
|
|
64
|
+
bordered: {
|
|
65
|
+
type: 'boolean',
|
|
66
|
+
default: true,
|
|
67
|
+
description: 'Whether the editor renders with a border.'
|
|
68
|
+
},
|
|
69
|
+
disabled: {
|
|
70
|
+
type: 'boolean',
|
|
71
|
+
default: false,
|
|
72
|
+
description: 'Render the editor as read-only.'
|
|
73
|
+
},
|
|
74
|
+
highlight: {
|
|
75
|
+
type: 'object',
|
|
76
|
+
description: 'Text highlight extension settings.',
|
|
77
|
+
additionalProperties: false,
|
|
78
|
+
properties: {
|
|
79
|
+
disabled: {
|
|
80
|
+
type: 'boolean',
|
|
81
|
+
default: false
|
|
82
|
+
},
|
|
83
|
+
multicolor: {
|
|
84
|
+
type: 'boolean',
|
|
85
|
+
default: true
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
image: {
|
|
90
|
+
type: 'object',
|
|
91
|
+
description: 'Image extension settings.',
|
|
92
|
+
additionalProperties: false,
|
|
93
|
+
properties: {
|
|
94
|
+
disabled: {
|
|
95
|
+
type: 'boolean',
|
|
96
|
+
default: false
|
|
97
|
+
},
|
|
98
|
+
maxWidth: {
|
|
99
|
+
type: 'string',
|
|
100
|
+
default: '80%'
|
|
101
|
+
},
|
|
102
|
+
zoom: {
|
|
103
|
+
type: 'number',
|
|
104
|
+
default: 0.6
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
inputStyle: {
|
|
109
|
+
type: 'object',
|
|
110
|
+
description: 'Inline style applied to the editable area of the editor.',
|
|
111
|
+
docs: {
|
|
112
|
+
displayType: 'yaml'
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
label: {
|
|
116
|
+
type: 'object',
|
|
117
|
+
description: 'Label configuration forwarded to the Lowdefy Label block.',
|
|
118
|
+
docs: {
|
|
119
|
+
displayType: 'yaml'
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
link: {
|
|
123
|
+
type: 'object',
|
|
124
|
+
description: 'Link extension settings.',
|
|
125
|
+
additionalProperties: false,
|
|
126
|
+
properties: {
|
|
127
|
+
disabled: {
|
|
128
|
+
type: 'boolean',
|
|
129
|
+
default: false
|
|
130
|
+
},
|
|
131
|
+
autolink: {
|
|
132
|
+
type: 'boolean',
|
|
133
|
+
default: true
|
|
134
|
+
},
|
|
135
|
+
linkOnPaste: {
|
|
136
|
+
type: 'boolean',
|
|
137
|
+
default: true
|
|
138
|
+
},
|
|
139
|
+
openOnClick: {
|
|
140
|
+
type: 'boolean',
|
|
141
|
+
default: true
|
|
142
|
+
},
|
|
143
|
+
defaultProtocol: {
|
|
144
|
+
type: 'string',
|
|
145
|
+
default: 'https'
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
mentions: {
|
|
150
|
+
type: 'object',
|
|
151
|
+
description: 'Configure the set of mention targets and how they render.',
|
|
152
|
+
additionalProperties: false,
|
|
153
|
+
properties: {
|
|
154
|
+
char: {
|
|
155
|
+
type: 'string',
|
|
156
|
+
default: '@',
|
|
157
|
+
description: 'Trigger character that opens the mention dropdown. Change to "#" for hashtags, etc.'
|
|
158
|
+
},
|
|
159
|
+
allowSpaces: {
|
|
160
|
+
type: 'boolean',
|
|
161
|
+
default: true,
|
|
162
|
+
description: 'Allow spaces inside a mention query before it is committed.'
|
|
163
|
+
},
|
|
164
|
+
options: {
|
|
165
|
+
type: 'array',
|
|
166
|
+
description: 'Array of mention items. Each item may be a string, or an object with a "label" (matched against user input) and a "value" (stored on the node).'
|
|
167
|
+
},
|
|
168
|
+
getHref: {
|
|
169
|
+
type: 'object',
|
|
170
|
+
description: 'Optional function (_function operator) that receives a selected mention id and returns an href. When provided, mentions render as <a> tags.',
|
|
171
|
+
docs: {
|
|
172
|
+
displayType: 'yaml'
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
mentionsRequestId: {
|
|
178
|
+
type: 'string',
|
|
179
|
+
description: 'Id of a request used to populate mention options. When set, the block registers a __getTipTapMentions event that calls that request.'
|
|
180
|
+
},
|
|
181
|
+
placeholder: {
|
|
182
|
+
type: 'string',
|
|
183
|
+
description: 'Placeholder shown when the editor is empty.'
|
|
184
|
+
},
|
|
185
|
+
rows: {
|
|
186
|
+
type: 'integer',
|
|
187
|
+
minimum: 1,
|
|
188
|
+
description: 'Fix the editor height to exactly this many rows. Takes precedence over autoSize.'
|
|
189
|
+
},
|
|
190
|
+
s3PostPolicyRequestId: {
|
|
191
|
+
type: 'string',
|
|
192
|
+
description: 'Id of a request that returns an S3 presigned POST policy. When set, images dragged or pasted into the editor are uploaded via that policy.'
|
|
193
|
+
},
|
|
194
|
+
size: {
|
|
195
|
+
type: 'string',
|
|
196
|
+
enum: [
|
|
197
|
+
'small',
|
|
198
|
+
'middle',
|
|
199
|
+
'large'
|
|
200
|
+
],
|
|
201
|
+
description: 'Label size forwarded to the Label block.'
|
|
202
|
+
},
|
|
203
|
+
starterKit: {
|
|
204
|
+
type: 'object',
|
|
205
|
+
description: 'Options forwarded to TipTap StarterKit. Use to disable bundled extensions (e.g. {heading: false}).',
|
|
206
|
+
docs: {
|
|
207
|
+
displayType: 'yaml'
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
table: {
|
|
211
|
+
type: 'object',
|
|
212
|
+
description: 'Table extension settings.',
|
|
213
|
+
additionalProperties: false,
|
|
214
|
+
properties: {
|
|
215
|
+
disabled: {
|
|
216
|
+
type: 'boolean',
|
|
217
|
+
default: false
|
|
218
|
+
},
|
|
219
|
+
resizable: {
|
|
220
|
+
type: 'boolean',
|
|
221
|
+
default: true
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
title: {
|
|
226
|
+
type: 'string',
|
|
227
|
+
description: 'Label title shown above the editor.'
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
};
|