@vonaffenfels/slate-editor 1.0.25 → 1.0.28
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/dist/BlockEditor.css +1 -1
- package/dist/BlockEditor.js +1 -1
- package/dist/Renderer.js +1 -1
- package/dist/index.css +1 -1
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/src/BlockEditor.js +9 -1
- package/src/Blocks/LayoutBlock.js +55 -0
- package/src/Serializer/Serializer.js +2 -0
- package/src/SidebarEditor/AssetList.js +17 -1
- package/src/SidebarEditor/SidebarEditorField.js +16 -3
- package/src/SidebarEditor.js +1 -1
- package/src/Toolbar/Block.js +16 -1
- package/src/Toolbar/Layout.js +67 -0
- package/src/plugins/ListItem.js +3 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vonaffenfels/slate-editor",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.28",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"url-loader": "^4.1.1",
|
|
63
63
|
"util": "^0.12.5",
|
|
64
64
|
"walk-sync": "^3.0.0",
|
|
65
|
-
"webpack": "5.
|
|
65
|
+
"webpack": "5.88.2",
|
|
66
66
|
"webpack-cli": "^4.6.0",
|
|
67
67
|
"webpack-dev-server": "4.7.4"
|
|
68
68
|
},
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"cssnano": "^5.0.1",
|
|
72
72
|
"escape-html": "^1.0.3"
|
|
73
73
|
},
|
|
74
|
-
"gitHead": "
|
|
74
|
+
"gitHead": "fe5a1e14e437b67a1aa58fa6781b9ae134914750",
|
|
75
75
|
"publishConfig": {
|
|
76
76
|
"access": "public"
|
|
77
77
|
}
|
package/src/BlockEditor.js
CHANGED
|
@@ -17,6 +17,8 @@ import "../scss/editor.scss";
|
|
|
17
17
|
import {ListItemPlugin} from "./plugins/ListItem";
|
|
18
18
|
import ErrorBoundary from "../src/Blocks/ErrorBoundary";
|
|
19
19
|
import SidebarEditor from './SidebarEditor';
|
|
20
|
+
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
|
21
|
+
import {faSpinner} from '@fortawesome/free-solid-svg-icons';
|
|
20
22
|
|
|
21
23
|
export default function BlockEditor({
|
|
22
24
|
onChange,
|
|
@@ -27,6 +29,7 @@ export default function BlockEditor({
|
|
|
27
29
|
storybookComponentDataLoader,
|
|
28
30
|
storybookStories,
|
|
29
31
|
onSaveClick,
|
|
32
|
+
isLoading,
|
|
30
33
|
}) {
|
|
31
34
|
const scrollContainer = useRef(null);
|
|
32
35
|
const [selectedStorybookElement, setSelectedStorybookElement] = useState(null);
|
|
@@ -231,7 +234,12 @@ export default function BlockEditor({
|
|
|
231
234
|
<div>
|
|
232
235
|
<Toolbar hover={false} onSaveClick={onSaveClick}/>
|
|
233
236
|
</div>
|
|
234
|
-
<div className="h-full max-h-full overflow-scroll px-8 py-4" ref={scrollContainer}>
|
|
237
|
+
<div className="relative h-full max-h-full overflow-scroll px-8 py-4" ref={scrollContainer}>
|
|
238
|
+
{isLoading && (
|
|
239
|
+
<div className='pointer-events-none fixed left-0 top-0 z-50 text-black dark:text-white' style={{padding: "61px 0 0 16px"}}>
|
|
240
|
+
<FontAwesomeIcon icon={faSpinner} pulse />
|
|
241
|
+
</div>
|
|
242
|
+
)}
|
|
235
243
|
<ErrorBoundary
|
|
236
244
|
name="editor"
|
|
237
245
|
fallback={(
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
|
3
3
|
import {
|
|
4
|
+
faAlignCenter,
|
|
4
5
|
faArrowDown, faArrowUp, faBorderAll, faBorderNone, faCompress, faTimes,
|
|
5
6
|
} from "@fortawesome/free-solid-svg-icons";
|
|
6
7
|
import {Transforms} from "slate";
|
|
@@ -119,6 +120,44 @@ export const LayoutBlock = ({
|
|
|
119
120
|
}, {at: fromPath});
|
|
120
121
|
};
|
|
121
122
|
|
|
123
|
+
const switchCenter = (e) => {
|
|
124
|
+
let node = ReactEditor.toSlateNode(editor, attributes.ref.current);
|
|
125
|
+
let fromPath = ReactEditor.findPath(editor, node);
|
|
126
|
+
|
|
127
|
+
if (!fromPath) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
let justifyCenter = !(element?.attributes?.justifyCenter);
|
|
132
|
+
|
|
133
|
+
Transforms.setNodes(editor, {
|
|
134
|
+
type: "layout",
|
|
135
|
+
attributes: {
|
|
136
|
+
...(element.attributes || {}),
|
|
137
|
+
justifyCenter,
|
|
138
|
+
},
|
|
139
|
+
}, {at: fromPath});
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const switchBorder = (e) => {
|
|
143
|
+
let node = ReactEditor.toSlateNode(editor, attributes.ref.current);
|
|
144
|
+
let fromPath = ReactEditor.findPath(editor, node);
|
|
145
|
+
|
|
146
|
+
if (!fromPath) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
let tableBorder = !(element?.attributes?.tableBorder);
|
|
151
|
+
|
|
152
|
+
Transforms.setNodes(editor, {
|
|
153
|
+
type: "layout",
|
|
154
|
+
attributes: {
|
|
155
|
+
...(element.attributes || {}),
|
|
156
|
+
tableBorder,
|
|
157
|
+
},
|
|
158
|
+
}, {at: fromPath});
|
|
159
|
+
};
|
|
160
|
+
|
|
122
161
|
const onMarginChange = (value) => {
|
|
123
162
|
let node = ReactEditor.toSlateNode(editor, attributes.ref.current);
|
|
124
163
|
let path = ReactEditor.findPath(editor, node);
|
|
@@ -159,12 +198,28 @@ export const LayoutBlock = ({
|
|
|
159
198
|
<FontAwesomeIcon icon={faBorderAll} size="lg"/>
|
|
160
199
|
)}
|
|
161
200
|
</span>
|
|
201
|
+
<span className="options-container-option options-container-option-expand" onClick={switchCenter}>
|
|
202
|
+
{!element?.attributes?.justifyCenter ? (
|
|
203
|
+
<FontAwesomeIcon icon={faAlignCenter} size="lg"/>
|
|
204
|
+
) : (
|
|
205
|
+
<FontAwesomeIcon icon={faAlignCenter} color="#3490f3" size="lg"/>
|
|
206
|
+
)}
|
|
207
|
+
</span>
|
|
208
|
+
<span className="options-container-option options-container-option-expand-text mx-2" onClick={switchBorder}>
|
|
209
|
+
{!element?.attributes?.tableBorder ? (
|
|
210
|
+
<span>Kein Rahmen</span>
|
|
211
|
+
) : (
|
|
212
|
+
<span>Rahmen</span>
|
|
213
|
+
)}
|
|
214
|
+
</span>
|
|
162
215
|
<ToolMargin margin={element?.attributes?.margin} onChange={onMarginChange}/>
|
|
163
216
|
</div>
|
|
164
217
|
<div
|
|
165
218
|
className={`layout-block layout-grid layout-grid-cols-${children.length} ` + classNames({
|
|
166
219
|
[classNameArticle + " article-width"]: element?.attributes?.width === "article" || !element?.attributes?.width,
|
|
167
220
|
[classNameSite + " site-width"]: element?.attributes?.width === "full",
|
|
221
|
+
"block-editor-table-border-wrapper": element?.attributes?.tableBorder,
|
|
222
|
+
"justify-center": element?.attributes?.justifyCenter,
|
|
168
223
|
"space-x-4": element?.attributes?.spacing,
|
|
169
224
|
"editor-mt-large": element?.attributes?.margin?.top,
|
|
170
225
|
"editor-mr-large": element?.attributes?.margin?.right,
|
|
@@ -137,6 +137,8 @@ export function Serializer({
|
|
|
137
137
|
"mr-16": props?.attributes?.margin?.right,
|
|
138
138
|
"mb-16": props?.attributes?.margin?.bottom,
|
|
139
139
|
"ml-16": props?.attributes?.margin?.left,
|
|
140
|
+
"block-editor-table-border-wrapper": props?.attributes?.tableBorder,
|
|
141
|
+
"justify-center": props?.attributes?.justifyCenter,
|
|
140
142
|
[typeProps.classNameSite + " site-width"]: !isInSlot && (props.attributes?.width === "site" || props.attributes?.width === "full"),
|
|
141
143
|
[typeProps.classNameArticle + " article-width"]: !isInSlot && (props.attributes?.width === "article" || !props.attributes?.width),
|
|
142
144
|
})}
|
|
@@ -107,6 +107,22 @@ export const Asset = ({
|
|
|
107
107
|
return null;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
let assetType = "image";
|
|
111
|
+
|
|
112
|
+
if (title?.endsWith(".mp3")) {
|
|
113
|
+
assetType = "audio";
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const renderMedia = () => {
|
|
117
|
+
switch (assetType) {
|
|
118
|
+
case "audio":
|
|
119
|
+
return <audio src={mediaUrl} className="mt-2 w-full" controls />;
|
|
120
|
+
case "image":
|
|
121
|
+
default:
|
|
122
|
+
return <img src={mediaUrl} width="100%" className="mt-2 rounded-md" />;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
110
126
|
return (
|
|
111
127
|
<div className="flex flex-col">
|
|
112
128
|
<div className="flex">
|
|
@@ -126,7 +142,7 @@ export const Asset = ({
|
|
|
126
142
|
<IconButton size="small" onClick={onDeleteClick}>⨯</IconButton>
|
|
127
143
|
</div>
|
|
128
144
|
</div>
|
|
129
|
-
{!!mediaUrl &&
|
|
145
|
+
{!!mediaUrl && renderMedia()}
|
|
130
146
|
</div>
|
|
131
147
|
);
|
|
132
148
|
};
|
|
@@ -36,6 +36,10 @@ export const SidebarEditorField = ({
|
|
|
36
36
|
};
|
|
37
37
|
|
|
38
38
|
const renderFieldSelectOptions = field => {
|
|
39
|
+
if (field?.control?.labels) {
|
|
40
|
+
field.control.options = field.control.labels;
|
|
41
|
+
}
|
|
42
|
+
|
|
39
43
|
if (!field?.control?.options) {
|
|
40
44
|
return;
|
|
41
45
|
}
|
|
@@ -118,8 +122,8 @@ export const SidebarEditorField = ({
|
|
|
118
122
|
case "object":
|
|
119
123
|
return (
|
|
120
124
|
<textarea
|
|
121
|
-
value={value || ""}
|
|
122
|
-
onChange={e => onChange(fieldKey, e.target.value)} />
|
|
125
|
+
value={typeof value === "object" ? JSON.stringify(value, null, 2) || "" : value}
|
|
126
|
+
onChange={e => onChange(fieldKey, isJson(e.target.value) ? JSON.parse(e.target.value) : e.target.value)} />
|
|
123
127
|
);
|
|
124
128
|
case "radio":
|
|
125
129
|
return (
|
|
@@ -375,8 +379,17 @@ export const SidebarEditorField = ({
|
|
|
375
379
|
|
|
376
380
|
return (
|
|
377
381
|
<div key={`${field.name}`} className="mb-2">
|
|
378
|
-
<label className="block">{field.name}</label>
|
|
382
|
+
<label className="block">{field.name || fieldKey}</label>
|
|
379
383
|
{inputField}
|
|
380
384
|
</div>
|
|
381
385
|
);
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
const isJson = (str) => {
|
|
389
|
+
try {
|
|
390
|
+
JSON.parse(str);
|
|
391
|
+
} catch (e) {
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
394
|
+
return true;
|
|
382
395
|
};
|
package/src/SidebarEditor.js
CHANGED
|
@@ -26,7 +26,7 @@ const SidebarEditor = ({
|
|
|
26
26
|
tables: {},
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
const story = storybookStories
|
|
29
|
+
const story = storybookStories?.find(v => storybookElement.block === v.id);
|
|
30
30
|
|
|
31
31
|
if (story) {
|
|
32
32
|
Object.keys(story.argTypes).forEach(key => {
|
package/src/Toolbar/Block.js
CHANGED
|
@@ -73,8 +73,23 @@ export const toggleBlock = (editor, format, attributes) => {
|
|
|
73
73
|
|
|
74
74
|
export const isBlockActive = (editor, format, attributes) => {
|
|
75
75
|
const {selection} = editor;
|
|
76
|
+
|
|
76
77
|
if (selection) {
|
|
77
|
-
|
|
78
|
+
let selected = editor;
|
|
79
|
+
|
|
80
|
+
let reducedPath = selection.anchor.path.slice(0, -1);
|
|
81
|
+
|
|
82
|
+
for (let i = 0; i < reducedPath.length; i++) {
|
|
83
|
+
let path = reducedPath[i];
|
|
84
|
+
|
|
85
|
+
selected = selected.children[path];
|
|
86
|
+
|
|
87
|
+
if (LIST_TYPES.includes(selected.type)) {
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const selectedNodes = [selected];
|
|
78
93
|
|
|
79
94
|
return !!selectedNodes.find(v => {
|
|
80
95
|
if (Editor.isEditor(v)) {
|
package/src/Toolbar/Layout.js
CHANGED
|
@@ -204,6 +204,73 @@ export const InsertGridButton = () => {
|
|
|
204
204
|
}}>
|
|
205
205
|
<span>1 | 1 | 1 | 1</span>
|
|
206
206
|
</InsertLayoutButton>
|
|
207
|
+
<InsertLayoutButton insert={{
|
|
208
|
+
"type": "layout",
|
|
209
|
+
"children": [
|
|
210
|
+
{
|
|
211
|
+
"type": "layout-slot",
|
|
212
|
+
"attributes": {"name": "grid-1"},
|
|
213
|
+
"children": [
|
|
214
|
+
{
|
|
215
|
+
"type": "paragraph",
|
|
216
|
+
"children": [
|
|
217
|
+
{"text": ""},
|
|
218
|
+
],
|
|
219
|
+
},
|
|
220
|
+
],
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
"type": "layout-slot",
|
|
224
|
+
"attributes": {"name": "grid-1"},
|
|
225
|
+
"children": [
|
|
226
|
+
{
|
|
227
|
+
"type": "paragraph",
|
|
228
|
+
"children": [
|
|
229
|
+
{"text": ""},
|
|
230
|
+
],
|
|
231
|
+
},
|
|
232
|
+
],
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
"type": "layout-slot",
|
|
236
|
+
"attributes": {"name": "grid-1"},
|
|
237
|
+
"children": [
|
|
238
|
+
{
|
|
239
|
+
"type": "paragraph",
|
|
240
|
+
"children": [
|
|
241
|
+
{"text": ""},
|
|
242
|
+
],
|
|
243
|
+
},
|
|
244
|
+
],
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
"type": "layout-slot",
|
|
248
|
+
"attributes": {"name": "grid-1"},
|
|
249
|
+
"children": [
|
|
250
|
+
{
|
|
251
|
+
"type": "paragraph",
|
|
252
|
+
"children": [
|
|
253
|
+
{"text": ""},
|
|
254
|
+
],
|
|
255
|
+
},
|
|
256
|
+
],
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
"type": "layout-slot",
|
|
260
|
+
"attributes": {"name": "grid-1"},
|
|
261
|
+
"children": [
|
|
262
|
+
{
|
|
263
|
+
"type": "paragraph",
|
|
264
|
+
"children": [
|
|
265
|
+
{"text": ""},
|
|
266
|
+
],
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
},
|
|
270
|
+
],
|
|
271
|
+
}}>
|
|
272
|
+
<span>1 | 1 | 1 | 1 | 1</span>
|
|
273
|
+
</InsertLayoutButton>
|
|
207
274
|
<InsertLayoutButton insert={{
|
|
208
275
|
"type": "layout",
|
|
209
276
|
"children": [
|
package/src/plugins/ListItem.js
CHANGED
|
@@ -21,9 +21,9 @@ export const ListItemPlugin = {
|
|
|
21
21
|
event.preventDefault();
|
|
22
22
|
Transforms.insertNodes(editor, {
|
|
23
23
|
at: editor.selection.anchor.path,
|
|
24
|
-
match: node => {
|
|
25
|
-
|
|
26
|
-
},
|
|
24
|
+
// match: node => {
|
|
25
|
+
// return node.type === "list-item";
|
|
26
|
+
// },
|
|
27
27
|
type: "list-item",
|
|
28
28
|
children: [{text: ""}],
|
|
29
29
|
});
|