@vonaffenfels/slate-editor 1.1.0 → 1.1.5
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 +335 -1
- package/dist/index.css +2 -2
- package/dist/index.js +335 -1
- package/package.json +3 -2
- package/scss/editor.scss +15 -0
- package/scss/storybook.scss +1 -0
- package/src/BlockEditor.js +1 -4
- package/src/ElementAutocomplete.js +43 -14
- package/src/SidebarEditor/Resizable.js +4 -1
- package/src/SidebarEditor.js +84 -48
- package/src/Toolbar/Toolbar.js +4 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vonaffenfels/slate-editor",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
|
26
26
|
"@babel/preset-env": "^7.13.15",
|
|
27
27
|
"@babel/preset-react": "^7.13.13",
|
|
28
|
+
"@contentful/f36-components": "^4.53.0",
|
|
28
29
|
"@contentful/forma-36-react-components": "^3.100.7",
|
|
29
30
|
"@fortawesome/fontawesome-svg-core": "^1.2.35",
|
|
30
31
|
"@fortawesome/free-solid-svg-icons": "^5.15.3",
|
|
@@ -71,7 +72,7 @@
|
|
|
71
72
|
"cssnano": "^5.0.1",
|
|
72
73
|
"escape-html": "^1.0.3"
|
|
73
74
|
},
|
|
74
|
-
"gitHead": "
|
|
75
|
+
"gitHead": "5f3a6d1e5724c6de129b8c1620973194d183ecbc",
|
|
75
76
|
"publishConfig": {
|
|
76
77
|
"access": "public"
|
|
77
78
|
}
|
package/scss/editor.scss
CHANGED
|
@@ -56,6 +56,10 @@
|
|
|
56
56
|
padding: 45px 0 0 0;
|
|
57
57
|
position: relative;
|
|
58
58
|
|
|
59
|
+
.editor-empty-element {
|
|
60
|
+
display: block!important;
|
|
61
|
+
}
|
|
62
|
+
|
|
59
63
|
p[data-slate-node="element"] {
|
|
60
64
|
position: relative;
|
|
61
65
|
//outline: #CECECE dashed;
|
|
@@ -427,11 +431,22 @@
|
|
|
427
431
|
}
|
|
428
432
|
|
|
429
433
|
.resizable {
|
|
434
|
+
|
|
435
|
+
&.enabled {
|
|
436
|
+
.resizable-left {
|
|
437
|
+
max-width: calc(100vw - 300px);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
430
441
|
.resizable-left {
|
|
442
|
+
scrollbar-width: none;
|
|
443
|
+
max-width: 100vw;
|
|
431
444
|
position: relative;
|
|
432
445
|
}
|
|
433
446
|
|
|
434
447
|
.resizable-right {
|
|
448
|
+
scrollbar-width: none;
|
|
449
|
+
max-width: calc(100vw - 300px);
|
|
435
450
|
position: relative;
|
|
436
451
|
}
|
|
437
452
|
|
package/scss/storybook.scss
CHANGED
package/src/BlockEditor.js
CHANGED
|
@@ -341,10 +341,7 @@ export default function BlockEditor({
|
|
|
341
341
|
Transforms.insertNodes(editor, [
|
|
342
342
|
{
|
|
343
343
|
children: [{text: ''}],
|
|
344
|
-
|
|
345
|
-
attributes: {blockWidth: "site"},
|
|
346
|
-
isEmpty: true,
|
|
347
|
-
type: "storybook",
|
|
344
|
+
type: "paragraph",
|
|
348
345
|
},
|
|
349
346
|
], {at});
|
|
350
347
|
};
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {Autocomplete} from "@contentful/
|
|
1
|
+
import {Autocomplete} from "@contentful/f36-components";
|
|
2
2
|
import {
|
|
3
3
|
useEffect, useState,
|
|
4
4
|
} from "react";
|
|
5
|
-
import {Transforms} from "slate";
|
|
6
5
|
|
|
7
6
|
const devMode = localStorage.getItem("dev-mode") === "true";
|
|
8
7
|
|
|
@@ -21,6 +20,8 @@ export const ElementAutocomplete = ({
|
|
|
21
20
|
type: "storybook",
|
|
22
21
|
})) || []), ...customStories]).map(story => {
|
|
23
22
|
let storyTitleSplit = String(story.title || "").split("/");
|
|
23
|
+
let storyName = story.label || storyTitleSplit.at(-1);
|
|
24
|
+
let storyGroup = storyTitleSplit.length > 1 ? storyTitleSplit.slice(0, -1).join("/") : null;
|
|
24
25
|
|
|
25
26
|
if (story.type === "storybook") {
|
|
26
27
|
if (!story.id) {
|
|
@@ -38,10 +39,10 @@ export const ElementAutocomplete = ({
|
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
|
|
42
42
|
return {
|
|
43
43
|
value: story.id?.toLowerCase(),
|
|
44
|
-
|
|
44
|
+
group: storyGroup,
|
|
45
|
+
label: (storyName || "NameFehlerhaft").replace(/([^A-Z]*)([A-Z]+)([^A-Z]*)/g, '$1 $2$3').trim(),
|
|
45
46
|
attributes: story?.stories?.[0]?.args || story?.stories?.[1]?.args || {},
|
|
46
47
|
type: story.type,
|
|
47
48
|
};
|
|
@@ -77,24 +78,52 @@ export const ElementAutocomplete = ({
|
|
|
77
78
|
}
|
|
78
79
|
}, []);
|
|
79
80
|
|
|
81
|
+
|
|
82
|
+
const groupedItemsObj = filteredItems.reduce((acc, item) => {
|
|
83
|
+
if (!acc[item.group]) {
|
|
84
|
+
acc[item.group] = [];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
acc[item.group].push(item);
|
|
88
|
+
|
|
89
|
+
return acc;
|
|
90
|
+
}, {});
|
|
91
|
+
const groupedItemsArr = Object.keys(groupedItemsObj).reduce((acc, key) => {
|
|
92
|
+
const group = {
|
|
93
|
+
groupTitle: key || "#",
|
|
94
|
+
options: groupedItemsObj[key],
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
group.options.sort((a, b) => {
|
|
98
|
+
return a.label.localeCompare(b.label);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
acc.push(group);
|
|
102
|
+
|
|
103
|
+
return acc;
|
|
104
|
+
}, []);
|
|
105
|
+
|
|
106
|
+
groupedItemsArr.sort((a, b) => {
|
|
107
|
+
return a.groupTitle.localeCompare(b.groupTitle);
|
|
108
|
+
});
|
|
109
|
+
|
|
80
110
|
return (
|
|
81
111
|
<Autocomplete
|
|
82
|
-
items={
|
|
83
|
-
|
|
112
|
+
items={groupedItemsArr}
|
|
113
|
+
isGrouped={true}
|
|
114
|
+
onInputValueChange={handleQueryChange}
|
|
84
115
|
isLoading={isLoading}
|
|
85
116
|
placeholder={'Element hinzufügen'}
|
|
86
|
-
emptyListMessage={'Keine
|
|
87
|
-
noMatchesMessage={'Keine
|
|
117
|
+
emptyListMessage={'Keine Elemente gefunden'}
|
|
118
|
+
noMatchesMessage={'Keine Elemente gefunden'}
|
|
88
119
|
dropdownProps={{isFullWidth: true}}
|
|
89
120
|
maxHeight={300}
|
|
90
|
-
|
|
121
|
+
onSelectItem={handleOnChange}
|
|
91
122
|
width="medium"
|
|
123
|
+
itemToString={(item) => ""}
|
|
124
|
+
renderItem={(item) => `${item.label}`}
|
|
92
125
|
{...props}
|
|
93
|
-
|
|
94
|
-
{(options) =>
|
|
95
|
-
options.map((option) => <span key={option.value}>{option.label}</span>)
|
|
96
|
-
}
|
|
97
|
-
</Autocomplete>
|
|
126
|
+
/>
|
|
98
127
|
);
|
|
99
128
|
};
|
|
100
129
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
useEffect, useRef,
|
|
3
3
|
} from "react";
|
|
4
|
+
import classNames from "classnames";
|
|
4
5
|
|
|
5
6
|
export const Resizable = ({
|
|
6
7
|
left,
|
|
@@ -73,7 +74,9 @@ export const Resizable = ({
|
|
|
73
74
|
}
|
|
74
75
|
}, [right, left]);
|
|
75
76
|
|
|
76
|
-
return <div
|
|
77
|
+
return <div
|
|
78
|
+
className={classNames("resizable flex w-full", {"enabled": !!right})}
|
|
79
|
+
ref={containerRef}>
|
|
77
80
|
<div ref={leftRef} className="resizable-left grow">{left}</div>
|
|
78
81
|
{right && <div ref={rightRef} className="resizable-right shrink-0">
|
|
79
82
|
<div ref={handleRef} className="resizable-x-handle"/>
|
package/src/SidebarEditor.js
CHANGED
|
@@ -5,7 +5,6 @@ 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
|
-
import {ElementAutocomplete} from "./ElementAutocomplete";
|
|
9
8
|
import {
|
|
10
9
|
CollapsableMenu, CollapsableMenuItem,
|
|
11
10
|
} from "./CollapsableMenu/CollapsableMenu";
|
|
@@ -198,7 +197,7 @@ const SidebarEditor = ({
|
|
|
198
197
|
|
|
199
198
|
let storyTitleSplit = String(story.title || "").split("/");
|
|
200
199
|
|
|
201
|
-
return <h2 className="mb-2 text-lg font-bold">{storyTitleSplit[storyTitleSplit.length - 1]}</h2>;
|
|
200
|
+
return <h2 className="mb-2 text-lg font-bold text-black">{storyTitleSplit[storyTitleSplit.length - 1]}</h2>;
|
|
202
201
|
};
|
|
203
202
|
|
|
204
203
|
const renderFields = () => {
|
|
@@ -291,7 +290,7 @@ const SidebarEditor = ({
|
|
|
291
290
|
<div id="sidebar-editor">
|
|
292
291
|
{isLoading ? (
|
|
293
292
|
<div className="flex h-full flex-col items-center justify-center">
|
|
294
|
-
<Spinner
|
|
293
|
+
<Spinner/>
|
|
295
294
|
<span className="mt-2">Elemente werden geladen</span>
|
|
296
295
|
</div>
|
|
297
296
|
) : (
|
|
@@ -304,32 +303,62 @@ const SidebarEditor = ({
|
|
|
304
303
|
<div className="mr-1 flex items-center">
|
|
305
304
|
<ToolMargin
|
|
306
305
|
margin={storybookElement.attributes.margin}
|
|
307
|
-
onChange={value => handleFieldValueChange("margin", value)}
|
|
306
|
+
onChange={value => handleFieldValueChange("margin", value)}/>
|
|
308
307
|
</div>
|
|
309
308
|
<div className="icon-button-group mr-1">
|
|
310
|
-
<IconButton
|
|
311
|
-
|
|
309
|
+
<IconButton
|
|
310
|
+
title="Rückgängig"
|
|
311
|
+
onClick={undo}
|
|
312
|
+
disabled={currentVersion <= 1}>↺</IconButton>
|
|
313
|
+
<IconButton
|
|
314
|
+
title="Wiederherstellen"
|
|
315
|
+
onClick={redo}
|
|
316
|
+
disabled={currentVersion >= versionCount || versionCount === 0}>↻</IconButton>
|
|
312
317
|
</div>
|
|
313
318
|
<div className="icon-button-group mr-1">
|
|
314
|
-
<IconButton
|
|
315
|
-
|
|
319
|
+
<IconButton
|
|
320
|
+
title="Nach oben verschieben"
|
|
321
|
+
onClick={() => onMove && onMove(storybookElement, "up")}
|
|
322
|
+
disabled={storybookElement.path[0] === 0}>
|
|
323
|
+
↑
|
|
316
324
|
</IconButton>
|
|
317
|
-
<IconButton
|
|
318
|
-
|
|
325
|
+
<IconButton
|
|
326
|
+
title="Nach unten verschieben"
|
|
327
|
+
onClick={() => onMove && onMove(storybookElement, "down")}
|
|
328
|
+
disabled={storybookElement.path[0] === editor.children.length - 1}>
|
|
329
|
+
↓
|
|
319
330
|
</IconButton>
|
|
320
331
|
</div>
|
|
321
|
-
<IconButton
|
|
322
|
-
|
|
332
|
+
<IconButton
|
|
333
|
+
title="Löschen"
|
|
334
|
+
className="mr-1"
|
|
335
|
+
onClick={() => onDelete && onDelete(storybookElement)}>
|
|
336
|
+
🗑
|
|
323
337
|
</IconButton>
|
|
324
338
|
<CollapsableMenu button={<IconButton title="Funktionen">…</IconButton>}>
|
|
325
|
-
<CollapsableMenuItem
|
|
326
|
-
|
|
327
|
-
<CollapsableMenuItem onClick={() => onInsert("
|
|
339
|
+
<CollapsableMenuItem
|
|
340
|
+
onClick={onDuplicate}>Duplizieren</CollapsableMenuItem>
|
|
341
|
+
<CollapsableMenuItem onClick={() => onInsert("above")}>Davor
|
|
342
|
+
hinzufügen</CollapsableMenuItem>
|
|
343
|
+
<CollapsableMenuItem onClick={() => onInsert("below")}>Danach
|
|
344
|
+
hinzufügen</CollapsableMenuItem>
|
|
328
345
|
<div className="px-4 pb-2 pt-1">
|
|
329
346
|
<b className="mb-1 block text-sm">Sichtbar auf:</b>
|
|
330
|
-
<Switch
|
|
331
|
-
|
|
332
|
-
|
|
347
|
+
<Switch
|
|
348
|
+
value={!storybookElement.attributes.hideOnDesktop}
|
|
349
|
+
label="Desktop"
|
|
350
|
+
className="mb-2"
|
|
351
|
+
onClick={() => handleFieldValueChange("hideOnDesktop", !storybookElement.attributes.hideOnDesktop)}/>
|
|
352
|
+
<Switch
|
|
353
|
+
value={!storybookElement.attributes.hideOnTablet}
|
|
354
|
+
label="Tablet"
|
|
355
|
+
className="mb-2"
|
|
356
|
+
onClick={() => handleFieldValueChange("hideOnTablet", !storybookElement.attributes.hideOnTablet)}/>
|
|
357
|
+
<Switch
|
|
358
|
+
value={!storybookElement.attributes.hideOnSmartphone}
|
|
359
|
+
label="Smartphone"
|
|
360
|
+
className="mb-2"
|
|
361
|
+
onClick={() => handleFieldValueChange("hideOnSmartphone", !storybookElement.attributes.hideOnSmartphone)}/>
|
|
333
362
|
</div>
|
|
334
363
|
</CollapsableMenu>
|
|
335
364
|
</div>
|
|
@@ -339,29 +368,25 @@ const SidebarEditor = ({
|
|
|
339
368
|
</div>
|
|
340
369
|
<hr className="mt-2" style={{borderColor: "#cfd9e0"}}/>
|
|
341
370
|
</div>
|
|
342
|
-
<div className="grow overflow-y-auto pr-2 pt-2">
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
<ElementAutocomplete
|
|
346
|
-
isLoading={isLoading}
|
|
347
|
-
storybookStories={storybookStories}
|
|
348
|
-
editor={editor}
|
|
349
|
-
storyContext={sdk.parameters.instance.storyContext}
|
|
350
|
-
portal={portal}
|
|
351
|
-
lastSelection={lastSelection}
|
|
352
|
-
onChange={handleAutocompleteChange}
|
|
353
|
-
placeholder="Element wählen"
|
|
354
|
-
width="full"
|
|
355
|
-
/>
|
|
371
|
+
<div className="flex grow flex-col gap-2 overflow-y-auto pr-2 pt-2" >
|
|
372
|
+
<div>
|
|
373
|
+
{renderTitle()}
|
|
356
374
|
</div>
|
|
357
375
|
{storybookElement?.block && (
|
|
358
|
-
|
|
359
|
-
<div className="mb-2
|
|
360
|
-
<VariantSelect
|
|
361
|
-
|
|
376
|
+
<>
|
|
377
|
+
<div className="mb-2 flex flex-col gap-2">
|
|
378
|
+
<VariantSelect
|
|
379
|
+
sdk={sdk}
|
|
380
|
+
className="w-full"
|
|
381
|
+
story={story}
|
|
382
|
+
onChange={handleVariantSelectChange}/>
|
|
383
|
+
<BlockWidthSelect
|
|
384
|
+
className="w-full"
|
|
385
|
+
value={storybookElement.attributes.blockWidth}
|
|
386
|
+
onChange={e => handleFieldValueChange("blockWidth", e.target.value)}/>
|
|
362
387
|
</div>
|
|
363
388
|
<hr className="my-4" style={{borderColor: "#cfd9e0"}}/>
|
|
364
|
-
|
|
389
|
+
</>
|
|
365
390
|
)}
|
|
366
391
|
{renderFields()}
|
|
367
392
|
</div>
|
|
@@ -377,7 +402,8 @@ export const IconButton = ({
|
|
|
377
402
|
disabled,
|
|
378
403
|
size = "medium",
|
|
379
404
|
className,
|
|
380
|
-
onClick = () => {
|
|
405
|
+
onClick = () => {
|
|
406
|
+
},
|
|
381
407
|
...props
|
|
382
408
|
}) => {
|
|
383
409
|
let classNames = "icon-button cursor-pointer select-none !p-1";
|
|
@@ -416,7 +442,6 @@ const BlockWidthSelect = ({
|
|
|
416
442
|
}) => {
|
|
417
443
|
return (
|
|
418
444
|
<div>
|
|
419
|
-
<label className="block">Breite</label>
|
|
420
445
|
<select
|
|
421
446
|
value={value || ""}
|
|
422
447
|
onChange={onChange}
|
|
@@ -435,24 +460,35 @@ const VariantSelect = ({
|
|
|
435
460
|
story,
|
|
436
461
|
onChange,
|
|
437
462
|
className,
|
|
463
|
+
sdk,
|
|
438
464
|
}) => {
|
|
439
465
|
const stories = story?.stories?.filter(s => s.title !== story.title) || [];
|
|
440
466
|
|
|
441
|
-
if (!onChange) {
|
|
467
|
+
if (!onChange || stories?.length < 2) {
|
|
442
468
|
return null;
|
|
443
469
|
}
|
|
444
470
|
|
|
445
471
|
return (
|
|
446
472
|
<div className={className}>
|
|
447
|
-
<label className="block">Preset</label>
|
|
448
473
|
<select
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
474
|
+
onChange={e => {
|
|
475
|
+
sdk.dialogs
|
|
476
|
+
.openConfirm({
|
|
477
|
+
title: 'Einstellungen überschreiben',
|
|
478
|
+
message: 'Alle Einstellungen werden überschrieben?',
|
|
479
|
+
intent: 'negative',
|
|
480
|
+
confirmLabel: 'Überschreiben',
|
|
481
|
+
cancelLabel: 'Abbrechen',
|
|
482
|
+
})
|
|
483
|
+
.then((result) => {
|
|
484
|
+
onChange({
|
|
485
|
+
block: story.id,
|
|
486
|
+
attributes: {...stories.find(s => s.title === e.target.value)?.args || {}},
|
|
487
|
+
type: "storybook",
|
|
488
|
+
});
|
|
489
|
+
});
|
|
490
|
+
}}>
|
|
491
|
+
<option>Einstellungen überschreiben</option>
|
|
456
492
|
{stories.map(s => (
|
|
457
493
|
<option key={`variant-option-${s.title}`} value={s.title}>{s.title}</option>
|
|
458
494
|
))}
|
package/src/Toolbar/Toolbar.js
CHANGED
|
@@ -100,7 +100,10 @@ export const Toolbar = ({
|
|
|
100
100
|
children: [{text: ''}],
|
|
101
101
|
type: item.type,
|
|
102
102
|
block: item.value,
|
|
103
|
-
attributes:
|
|
103
|
+
attributes: {
|
|
104
|
+
...(item.attributes || {}),
|
|
105
|
+
blockWidth: "site",
|
|
106
|
+
},
|
|
104
107
|
};
|
|
105
108
|
|
|
106
109
|
Transforms.insertNodes(editor, [element], {at: [lastSelection?.anchor?.path?.[0]]});
|