@vonaffenfels/slate-editor 1.0.60 → 1.0.63
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 +2 -2
- package/dist/BlockEditor.js +1 -1
- package/dist/index.css +2 -2
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/scss/editor.scss +14 -6
- package/scss/sidebarEditor.scss +20 -0
- package/src/BlockEditor.js +49 -2
- package/src/CollapsableMenu/CollapsableMenu.js +46 -0
- package/src/ElementAutocomplete.js +84 -0
- package/src/SidebarEditor/Resizable.js +18 -13
- package/src/SidebarEditor.js +58 -115
- package/src/Toolbar/Toolbar.js +13 -83
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vonaffenfels/slate-editor",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.63",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"cssnano": "^5.0.1",
|
|
72
72
|
"escape-html": "^1.0.3"
|
|
73
73
|
},
|
|
74
|
-
"gitHead": "
|
|
74
|
+
"gitHead": "fe0fcf870d95e0f1c3cc6e8edd31a4c70465df63",
|
|
75
75
|
"publishConfig": {
|
|
76
76
|
"access": "public"
|
|
77
77
|
}
|
package/scss/editor.scss
CHANGED
|
@@ -397,15 +397,9 @@
|
|
|
397
397
|
}
|
|
398
398
|
|
|
399
399
|
.button.button--secondary {
|
|
400
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !important;
|
|
401
|
-
width: 100%;
|
|
402
|
-
padding: 8px 0.75rem !important;
|
|
403
400
|
color: #036fe3 !important;
|
|
404
401
|
border: 1px solid #036fe3 !important;
|
|
405
|
-
transition: all 100ms ease-in-out;
|
|
406
402
|
background-color: transparent !important;
|
|
407
|
-
font-size: 0.875rem !important;
|
|
408
|
-
border-radius: 4px !important;
|
|
409
403
|
}
|
|
410
404
|
|
|
411
405
|
.button.button--secondary:hover {
|
|
@@ -418,6 +412,20 @@
|
|
|
418
412
|
color: #FFFFFF !important;
|
|
419
413
|
}
|
|
420
414
|
|
|
415
|
+
.button.button--tertiary {
|
|
416
|
+
color: rgb(90, 101, 124) !important;
|
|
417
|
+
background-color: white !important;
|
|
418
|
+
border: 1px solid #cfd9e0 !important;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.button.button--tertiary:hover {
|
|
422
|
+
background-color: rgb(248, 248, 248) !important;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.button.button--tertiary:active {
|
|
426
|
+
background-color: rgb(237, 237, 237) !important;
|
|
427
|
+
}
|
|
428
|
+
|
|
421
429
|
.resizable {
|
|
422
430
|
.resizable-left {
|
|
423
431
|
position: relative;
|
package/scss/sidebarEditor.scss
CHANGED
|
@@ -156,4 +156,24 @@
|
|
|
156
156
|
.message.message--negative {
|
|
157
157
|
background-color: rgba(238, 26, 26, 0.2);
|
|
158
158
|
}
|
|
159
|
+
|
|
160
|
+
.collapsable-menu {
|
|
161
|
+
background-color: white !important;
|
|
162
|
+
border: 1px solid #cfd9e0 !important;
|
|
163
|
+
border-radius: 6px;
|
|
164
|
+
z-index: 2;
|
|
165
|
+
overflow: hidden;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.collapsable-menu-item {
|
|
169
|
+
color: rgb(90, 101, 124) !important;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.collapsable-menu-item:not(.disabled):hover {
|
|
173
|
+
background-color: rgb(248, 248, 248) !important;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.collapsable-menu-item:not(.disabled):active {
|
|
177
|
+
background-color: rgb(237, 237, 237) !important;
|
|
178
|
+
}
|
|
159
179
|
}
|
package/src/BlockEditor.js
CHANGED
|
@@ -204,7 +204,7 @@ export default function BlockEditor({
|
|
|
204
204
|
setSelectedStorybookElement(mergedValue);
|
|
205
205
|
};
|
|
206
206
|
|
|
207
|
-
const handleSidebarDeleteClick = (
|
|
207
|
+
const handleSidebarDeleteClick = () => {
|
|
208
208
|
contentfulSdk.dialogs
|
|
209
209
|
.openConfirm({
|
|
210
210
|
title: 'Element löschen',
|
|
@@ -271,6 +271,50 @@ export default function BlockEditor({
|
|
|
271
271
|
}
|
|
272
272
|
};
|
|
273
273
|
|
|
274
|
+
const handleSidebarDuplicateClick = () => {
|
|
275
|
+
let path = selectedStorybookElement.path;
|
|
276
|
+
|
|
277
|
+
let selectedStorybookElementCopy = JSON.parse(JSON.stringify({
|
|
278
|
+
...selectedStorybookElement,
|
|
279
|
+
editorAttributes: null,
|
|
280
|
+
path: null,
|
|
281
|
+
node: null,
|
|
282
|
+
}));
|
|
283
|
+
|
|
284
|
+
delete selectedStorybookElementCopy.path;
|
|
285
|
+
delete selectedStorybookElementCopy.node;
|
|
286
|
+
delete selectedStorybookElementCopy.editorAttributes;
|
|
287
|
+
|
|
288
|
+
try {
|
|
289
|
+
Transforms.insertNodes(editor, [selectedStorybookElementCopy], {at: path});
|
|
290
|
+
} catch (e) {
|
|
291
|
+
console.error(e);
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
const handleSidebarInsertClick = direction => {
|
|
296
|
+
let node = ReactEditor.toSlateNode(editor, selectedStorybookElement.editorAttributes.ref.current);
|
|
297
|
+
let path = ReactEditor.findPath(editor, node);
|
|
298
|
+
|
|
299
|
+
let at;
|
|
300
|
+
|
|
301
|
+
if (direction === "above") {
|
|
302
|
+
at = [path[0]];
|
|
303
|
+
} else {
|
|
304
|
+
at = [path[0] + 1];
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
Transforms.insertNodes(editor, [
|
|
308
|
+
{
|
|
309
|
+
children: [{text: ''}],
|
|
310
|
+
block: "Blocks/Empty",
|
|
311
|
+
attributes: {blockWidth: "site"},
|
|
312
|
+
isEmpty: true,
|
|
313
|
+
type: "storybook",
|
|
314
|
+
},
|
|
315
|
+
], {at});
|
|
316
|
+
};
|
|
317
|
+
|
|
274
318
|
const handleSidebarClose = () => {
|
|
275
319
|
setSelectedStorybookElement(null);
|
|
276
320
|
};
|
|
@@ -337,7 +381,10 @@ export default function BlockEditor({
|
|
|
337
381
|
onClose={handleSidebarClose}
|
|
338
382
|
onDelete={handleSidebarDeleteClick}
|
|
339
383
|
onMove={handleSidebarMoveClick}
|
|
340
|
-
|
|
384
|
+
onDuplicate={handleSidebarDuplicateClick}
|
|
385
|
+
onInsert={handleSidebarInsertClick}
|
|
386
|
+
editor={editor}
|
|
387
|
+
lastSelection={lastSelection} />
|
|
341
388
|
}
|
|
342
389
|
/>
|
|
343
390
|
</div>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {useState} from "react";
|
|
2
|
+
|
|
3
|
+
export const CollapsableMenu = ({
|
|
4
|
+
button,
|
|
5
|
+
children,
|
|
6
|
+
...props
|
|
7
|
+
}) => {
|
|
8
|
+
const [collapsed, setCollapsed] = useState(true);
|
|
9
|
+
|
|
10
|
+
children = children.map(child => ({
|
|
11
|
+
...child,
|
|
12
|
+
props: {
|
|
13
|
+
...child.props,
|
|
14
|
+
onClick: () => {
|
|
15
|
+
child.props.onClick();
|
|
16
|
+
setCollapsed(true);
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div className="relative">
|
|
23
|
+
<div className="relative">
|
|
24
|
+
<div onClick={() => setCollapsed(!collapsed)}>
|
|
25
|
+
{button}
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
{!collapsed && (
|
|
29
|
+
<div className="collapsable-menu absolute right-0 mt-2 w-52">
|
|
30
|
+
{children}
|
|
31
|
+
</div>
|
|
32
|
+
)}
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const CollapsableMenuItem = ({
|
|
38
|
+
children,
|
|
39
|
+
onClick,
|
|
40
|
+
}) => {
|
|
41
|
+
return (
|
|
42
|
+
<div className="collapsable-menu-item cursor-pointer px-4 py-2" onClick={onClick}>
|
|
43
|
+
{children}
|
|
44
|
+
</div>
|
|
45
|
+
);
|
|
46
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import {Autocomplete} from "@contentful/forma-36-react-components";
|
|
2
|
+
import {
|
|
3
|
+
useEffect, useState,
|
|
4
|
+
} from "react";
|
|
5
|
+
import {Transforms} from "slate";
|
|
6
|
+
|
|
7
|
+
const devMode = localStorage.getItem("dev-mode") === "true";
|
|
8
|
+
|
|
9
|
+
export const ElementAutocomplete = ({
|
|
10
|
+
storybookStories,
|
|
11
|
+
isLoading,
|
|
12
|
+
editor,
|
|
13
|
+
storyContext = "",
|
|
14
|
+
portal,
|
|
15
|
+
lastSelection,
|
|
16
|
+
onChange,
|
|
17
|
+
...props
|
|
18
|
+
}) => {
|
|
19
|
+
const items = (storybookStories || []).map(story => {
|
|
20
|
+
let storyTitleSplit = String(story.title || "").split("/");
|
|
21
|
+
|
|
22
|
+
if (!story.id) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let splitStoryContext = String(storyContext || "").split(",");
|
|
27
|
+
let isItemInStoryContext = splitStoryContext.find(context => {
|
|
28
|
+
return Array.isArray(story.storyContext) ? story.storyContext.includes(context) : context === story.storyContext;
|
|
29
|
+
});
|
|
30
|
+
let isItemInPortalContext = !story.portalContext || story.portalContext.includes(portal);
|
|
31
|
+
|
|
32
|
+
if (!devMode && (!isItemInStoryContext || !isItemInPortalContext)) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
value: story.id.toLowerCase(),
|
|
38
|
+
label: storyTitleSplit[storyTitleSplit.length - 1],
|
|
39
|
+
stories: story.stories,
|
|
40
|
+
};
|
|
41
|
+
}).filter(Boolean);
|
|
42
|
+
|
|
43
|
+
const [filteredItems, setFilteredItems] = useState(items);
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
setFilteredItems(items);
|
|
47
|
+
}, [storybookStories]);
|
|
48
|
+
|
|
49
|
+
const handleQueryChange = (query) => {
|
|
50
|
+
setFilteredItems(query ? items.filter((item) => item.label.toLowerCase().includes(query.toLowerCase())) : items);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const handleOnChange = (item) => {
|
|
54
|
+
onChange(item);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
let autoCompleteElement = document.getElementsByClassName("element-autocomplete")[0];
|
|
59
|
+
|
|
60
|
+
if (autoCompleteElement) {
|
|
61
|
+
autoCompleteElement.value = "";
|
|
62
|
+
}
|
|
63
|
+
}, []);
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<Autocomplete
|
|
67
|
+
items={filteredItems}
|
|
68
|
+
onQueryChange={handleQueryChange}
|
|
69
|
+
isLoading={isLoading}
|
|
70
|
+
placeholder={'Element hinzufügen'}
|
|
71
|
+
emptyListMessage={'Keine Komponenten gefunden'}
|
|
72
|
+
noMatchesMessage={'Keine Ergebnisse gefunden'}
|
|
73
|
+
dropdownProps={{isFullWidth: true}}
|
|
74
|
+
maxHeight={300}
|
|
75
|
+
onChange={handleOnChange}
|
|
76
|
+
width="medium"
|
|
77
|
+
{...props}
|
|
78
|
+
>
|
|
79
|
+
{(options) =>
|
|
80
|
+
options.map((option) => <span key={option.value}>{option.label}</span>)
|
|
81
|
+
}
|
|
82
|
+
</Autocomplete>
|
|
83
|
+
);
|
|
84
|
+
};
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
useEffect, useRef,
|
|
3
|
+
} from "react";
|
|
2
4
|
|
|
3
|
-
export const Resizable = ({
|
|
5
|
+
export const Resizable = ({
|
|
6
|
+
left,
|
|
7
|
+
right,
|
|
8
|
+
}) => {
|
|
4
9
|
const containerRef = useRef();
|
|
5
10
|
const leftRef = useRef();
|
|
6
11
|
const handleRef = useRef();
|
|
@@ -14,11 +19,11 @@ export const Resizable = ({left, right}) => {
|
|
|
14
19
|
if (width < 300) {
|
|
15
20
|
return 300;
|
|
16
21
|
} else if (containerRef.current.getBoundingClientRect().width - width < 300) {
|
|
17
|
-
return containerRef.current.getBoundingClientRect().width - 300
|
|
22
|
+
return containerRef.current.getBoundingClientRect().width - 300;
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
return width;
|
|
21
|
-
}
|
|
26
|
+
};
|
|
22
27
|
|
|
23
28
|
useEffect(() => {
|
|
24
29
|
if (rightRef.current) {
|
|
@@ -41,18 +46,18 @@ export const Resizable = ({left, right}) => {
|
|
|
41
46
|
return;
|
|
42
47
|
}
|
|
43
48
|
|
|
44
|
-
let newWidth = rightRef.current.resizingStartWidth - (e.pageX - rightRef.current.resizingStartMouseX)
|
|
49
|
+
let newWidth = rightRef.current.resizingStartWidth - (e.pageX - rightRef.current.resizingStartMouseX);
|
|
45
50
|
newWidth = checkWidth(newWidth);
|
|
46
51
|
|
|
47
52
|
localStorage.setItem("slate-editor-resizable-width", newWidth);
|
|
48
53
|
|
|
49
54
|
rightRef.current.style.width = newWidth + 'px';
|
|
50
|
-
}
|
|
55
|
+
};
|
|
51
56
|
const onMouseUp = (e) => {
|
|
52
57
|
if (rightRef.current) {
|
|
53
58
|
rightRef.current.resizing = false;
|
|
54
59
|
}
|
|
55
|
-
}
|
|
60
|
+
};
|
|
56
61
|
|
|
57
62
|
rightRef.current.addEventListener("mousedown", onMouseDown);
|
|
58
63
|
document.addEventListener("mousemove", onMouseMove);
|
|
@@ -64,15 +69,15 @@ export const Resizable = ({left, right}) => {
|
|
|
64
69
|
}
|
|
65
70
|
document.removeEventListener("mousemove", onMouseMove);
|
|
66
71
|
document.removeEventListener("mouseup", onMouseUp);
|
|
67
|
-
}
|
|
72
|
+
};
|
|
68
73
|
}
|
|
69
74
|
}, [right, left]);
|
|
70
75
|
|
|
71
|
-
return <div className="w-full
|
|
72
|
-
<div ref={leftRef} className="resizable-left
|
|
73
|
-
{right && <div ref={rightRef} className="resizable-right">
|
|
76
|
+
return <div className="resizable flex w-full" ref={containerRef}>
|
|
77
|
+
<div ref={leftRef} className="resizable-left grow">{left}</div>
|
|
78
|
+
{right && <div ref={rightRef} className="resizable-right shrink-0">
|
|
74
79
|
<div ref={handleRef} className="resizable-x-handle"/>
|
|
75
80
|
{right}
|
|
76
81
|
</div>}
|
|
77
|
-
</div
|
|
78
|
-
}
|
|
82
|
+
</div>;
|
|
83
|
+
};
|
package/src/SidebarEditor.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
useState, useEffect,
|
|
3
3
|
} from "react";
|
|
4
4
|
import {SidebarEditorField} from "./SidebarEditor/SidebarEditorField";
|
|
5
5
|
import {ToolMargin} from "./Tools/Margin";
|
|
6
6
|
import "../scss/sidebarEditor.scss";
|
|
7
7
|
import {Spinner} from "@contentful/forma-36-react-components";
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
import {ElementAutocomplete} from "./ElementAutocomplete";
|
|
9
|
+
import {
|
|
10
|
+
CollapsableMenu, CollapsableMenuItem,
|
|
11
|
+
} from "./CollapsableMenu/CollapsableMenu";
|
|
10
12
|
|
|
11
13
|
const SidebarEditor = ({
|
|
12
14
|
sdk,
|
|
@@ -16,9 +18,14 @@ const SidebarEditor = ({
|
|
|
16
18
|
onChange,
|
|
17
19
|
onDelete,
|
|
18
20
|
onMove,
|
|
21
|
+
onDuplicate,
|
|
22
|
+
onInsert,
|
|
19
23
|
editor,
|
|
20
24
|
isLoading,
|
|
25
|
+
lastSelection,
|
|
21
26
|
}) => {
|
|
27
|
+
const portal = sdk?.entry?.fields?.portal.getValue();
|
|
28
|
+
|
|
22
29
|
const [versions, setVersions] = useState([]);
|
|
23
30
|
const [lastChangedProperty, setLastChangedProperty] = useState(null);
|
|
24
31
|
const [versionCount, setVersionCount] = useState(0);
|
|
@@ -164,8 +171,33 @@ const SidebarEditor = ({
|
|
|
164
171
|
}
|
|
165
172
|
};
|
|
166
173
|
|
|
174
|
+
const handleAutocompleteChange = (item) => {
|
|
175
|
+
if (!item) {
|
|
176
|
+
return onChange({}); // reset to empty
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// reset to the first or second story
|
|
180
|
+
onChange({
|
|
181
|
+
...storybookElement,
|
|
182
|
+
block: item.value,
|
|
183
|
+
attributes: {...(item?.stories?.[0]?.args || item?.stories?.[1]?.args || {})},
|
|
184
|
+
});
|
|
185
|
+
};
|
|
186
|
+
|
|
167
187
|
useEffect(() => resetVersions, [storybookElement?.editorAttributes?.ref]);
|
|
168
188
|
|
|
189
|
+
const renderTitle = () => {
|
|
190
|
+
const story = storybookStories?.find(v => storybookElement.block?.toLowerCase() === v.id?.toLowerCase());
|
|
191
|
+
|
|
192
|
+
if (!story?.title) {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
let storyTitleSplit = String(story.title || "").split("/");
|
|
197
|
+
|
|
198
|
+
return <h2 className="mb-2 text-lg font-bold">{storyTitleSplit[storyTitleSplit.length - 1]}</h2>;
|
|
199
|
+
};
|
|
200
|
+
|
|
169
201
|
return (
|
|
170
202
|
<div id="sidebar-editor-wrapper">
|
|
171
203
|
<div id="sidebar-editor">
|
|
@@ -198,23 +230,35 @@ const SidebarEditor = ({
|
|
|
198
230
|
↓
|
|
199
231
|
</IconButton>
|
|
200
232
|
</div>
|
|
201
|
-
<IconButton title="Löschen" onClick={() => onDelete && onDelete(storybookElement)}>
|
|
233
|
+
<IconButton title="Löschen" className="mr-1" onClick={() => onDelete && onDelete(storybookElement)}>
|
|
202
234
|
🗑
|
|
203
235
|
</IconButton>
|
|
236
|
+
<CollapsableMenu button={<IconButton title="Funktionen">…</IconButton>}>
|
|
237
|
+
<CollapsableMenuItem onClick={onDuplicate}>Duplizieren</CollapsableMenuItem>
|
|
238
|
+
<CollapsableMenuItem onClick={() => onInsert("above")}>Davor hinzufügen</CollapsableMenuItem>
|
|
239
|
+
<CollapsableMenuItem onClick={() => onInsert("below")}>Danach hinzufügen</CollapsableMenuItem>
|
|
240
|
+
</CollapsableMenu>
|
|
204
241
|
</div>
|
|
205
242
|
)}
|
|
206
243
|
</div>
|
|
207
|
-
{!!onClose && <IconButton
|
|
244
|
+
{!!onClose && <IconButton onClick={onClose} title="Schließen">⨉</IconButton>}
|
|
208
245
|
</div>
|
|
209
246
|
<hr className="mt-2" style={{borderColor: "#cfd9e0"}}/>
|
|
210
247
|
</div>
|
|
211
248
|
<div className="grow overflow-y-auto pr-2 pt-2">
|
|
249
|
+
{renderTitle()}
|
|
212
250
|
<div className="mb-2">
|
|
213
|
-
<
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
storyContext={sdk.parameters.instance.storyContext}
|
|
251
|
+
<ElementAutocomplete
|
|
252
|
+
isLoading={isLoading}
|
|
253
|
+
storybookStories={storybookStories}
|
|
254
|
+
editor={editor}
|
|
255
|
+
storyContext={sdk.parameters.instance.storyContext}
|
|
256
|
+
portal={portal}
|
|
257
|
+
lastSelection={lastSelection}
|
|
258
|
+
onChange={handleAutocompleteChange}
|
|
259
|
+
placeholder="Element wählen"
|
|
260
|
+
width="full"
|
|
261
|
+
/>
|
|
218
262
|
</div>
|
|
219
263
|
{storybookElement?.block && (
|
|
220
264
|
<div>
|
|
@@ -285,6 +329,7 @@ export const IconButton = ({
|
|
|
285
329
|
disabled,
|
|
286
330
|
size = "medium",
|
|
287
331
|
className,
|
|
332
|
+
onClick = () => {},
|
|
288
333
|
...props
|
|
289
334
|
}) => {
|
|
290
335
|
let classNames = "icon-button cursor-pointer select-none !p-1";
|
|
@@ -310,7 +355,7 @@ export const IconButton = ({
|
|
|
310
355
|
}
|
|
311
356
|
|
|
312
357
|
return (
|
|
313
|
-
<div className={classNames} {...props}>
|
|
358
|
+
<div className={classNames} onClick={onClick} {...props}>
|
|
314
359
|
{children}
|
|
315
360
|
</div>
|
|
316
361
|
);
|
|
@@ -367,108 +412,6 @@ const VariantSelect = ({
|
|
|
367
412
|
);
|
|
368
413
|
};
|
|
369
414
|
|
|
370
|
-
|
|
371
|
-
stories,
|
|
372
|
-
active,
|
|
373
|
-
onChange,
|
|
374
|
-
className,
|
|
375
|
-
storyContext,
|
|
376
|
-
}) => {
|
|
377
|
-
if (!onChange) {
|
|
378
|
-
return null;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
const onSelectChange = (story) => {
|
|
382
|
-
if (!story) {
|
|
383
|
-
return onChange({}); // reset to empty
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
// reset to the first or second story
|
|
387
|
-
onChange({
|
|
388
|
-
...active,
|
|
389
|
-
block: story.id,
|
|
390
|
-
attributes: {...(story?.stories?.[0]?.args || story?.stories?.[1]?.args || {})},
|
|
391
|
-
});
|
|
392
|
-
};
|
|
393
|
-
|
|
394
|
-
function groupArrayToObject(array) {
|
|
395
|
-
const result = {};
|
|
396
|
-
|
|
397
|
-
array.forEach(item => {
|
|
398
|
-
if (!item?.id) {
|
|
399
|
-
return;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
let splitStoryContext = String(storyContext || "").split(",");
|
|
403
|
-
let isItemInContext = splitStoryContext.find(context => {
|
|
404
|
-
return Array.isArray(item.storyContext) ? item.storyContext.includes(context) : context === item.storyContext;
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
if (!devMode && !isItemInContext) {
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
const parts = item.title.split('/');
|
|
412
|
-
let currentLevel = result;
|
|
413
|
-
|
|
414
|
-
parts.forEach((part, index) => {
|
|
415
|
-
if (!currentLevel[part]) {
|
|
416
|
-
currentLevel[part] = {level: index};
|
|
417
|
-
}
|
|
418
|
-
currentLevel = currentLevel[part];
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
currentLevel.title = item.title;
|
|
422
|
-
currentLevel.shortTitle = parts[parts.length - 1];
|
|
423
|
-
currentLevel.id = item.id;
|
|
424
|
-
});
|
|
425
|
-
|
|
426
|
-
return result;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
let storyGroups = groupArrayToObject(stories);
|
|
430
|
-
|
|
431
|
-
const renderOptions = (obj) => Object.keys(obj).map((key, index) => {
|
|
432
|
-
const option = obj[key];
|
|
433
|
-
|
|
434
|
-
if (!option) {
|
|
435
|
-
return null;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
if (option.title && option.id) {
|
|
439
|
-
return (
|
|
440
|
-
<option key={option.id} value={option.id.toLowerCase()}>
|
|
441
|
-
{option.shortTitle}
|
|
442
|
-
</option>
|
|
443
|
-
);
|
|
444
|
-
} else if (typeof option === "object") {
|
|
445
|
-
return (
|
|
446
|
-
<Fragment key={`${key}-${index}`}>
|
|
447
|
-
<option
|
|
448
|
-
disabled
|
|
449
|
-
style={{
|
|
450
|
-
color: "rgba(0, 0, 0, 0.3)",
|
|
451
|
-
fontWeight: option.level === 0 ? "bold" : "normal",
|
|
452
|
-
}}>{key}</option>
|
|
453
|
-
{renderOptions(option)}
|
|
454
|
-
</Fragment>
|
|
455
|
-
);
|
|
456
|
-
}
|
|
457
|
-
});
|
|
458
|
-
|
|
459
|
-
return (
|
|
460
|
-
<div className={className}>
|
|
461
|
-
<label className="block">Element</label>
|
|
462
|
-
<select
|
|
463
|
-
onChange={e => onSelectChange(stories.find(s => s.id?.toLowerCase() === e.target.value))}
|
|
464
|
-
value={active?.block?.toLowerCase()}
|
|
465
|
-
className="font-bold"
|
|
466
|
-
>
|
|
467
|
-
<option>Element wählen</option>
|
|
468
|
-
{renderOptions(storyGroups)}
|
|
469
|
-
</select>
|
|
470
|
-
</div>
|
|
471
|
-
);
|
|
472
|
-
};
|
|
415
|
+
export default SidebarEditor;
|
|
473
416
|
|
|
474
|
-
|
|
417
|
+
// :)
|