@vonaffenfels/slate-editor 1.0.41 → 1.0.44

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vonaffenfels/slate-editor",
3
- "version": "1.0.41",
3
+ "version": "1.0.44",
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": "1ad8ddd858ff27ea19e568d79f189f20a2ef0488",
74
+ "gitHead": "9bcbf3b9b1002d4d41a6d23fb3ab6b567fc1de4f",
75
75
  "publishConfig": {
76
76
  "access": "public"
77
77
  }
package/scss/editor.scss CHANGED
@@ -390,7 +390,7 @@
390
390
  }
391
391
  }
392
392
 
393
- button {
393
+ .button {
394
394
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !important;
395
395
  width: 100%;
396
396
  padding: 8px 0.75rem !important;
@@ -402,15 +402,15 @@ button {
402
402
  border-radius: 4px !important;
403
403
  }
404
404
 
405
- button:hover {
405
+ .button:hover {
406
406
  background-color: #0275F0 !important;
407
407
  }
408
408
 
409
- button:active {
409
+ .button:active {
410
410
  background-color: #0262C9 !important;
411
411
  }
412
412
 
413
- button.button--secondary {
413
+ .button.button--secondary {
414
414
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !important;
415
415
  width: 100%;
416
416
  padding: 8px 0.75rem !important;
@@ -422,12 +422,12 @@ button.button--secondary {
422
422
  border-radius: 4px !important;
423
423
  }
424
424
 
425
- button.button--secondary:hover {
425
+ .button.button--secondary:hover {
426
426
  background-color: #036fe3 !important;
427
427
  color: #FFFFFF !important;
428
428
  }
429
429
 
430
- button.button--secondary:active {
430
+ .button.button--secondary:active {
431
431
  background-color: #0262C9 !important;
432
432
  color: #FFFFFF !important;
433
433
  }
@@ -60,7 +60,7 @@
60
60
 
61
61
  input[type="radio"],
62
62
  input[type="checkbox"] {
63
- margin-right: 4px;
63
+ margin-right: 8px;
64
64
  }
65
65
 
66
66
  option[disabled] {
@@ -67,14 +67,14 @@ export default function BlockEditor({
67
67
  ];
68
68
 
69
69
  const [loadedStorybookStories, setLoadedStorybookStories] = useState([]);
70
- const [isSidebarLoading, setIsSidebarLoading] = useState(false);
70
+ const [isLoadingStories, setIsLoadingStories] = useState(false);
71
71
 
72
72
  const loadStories = async () => {
73
- setIsSidebarLoading(true);
73
+ setIsLoadingStories(true);
74
74
 
75
75
  let loadedStories = await storybookStories();
76
76
 
77
- setIsSidebarLoading(false);
77
+ setIsLoadingStories(false);
78
78
 
79
79
  setLoadedStorybookStories(loadedStories);
80
80
  };
@@ -218,6 +218,17 @@ export default function BlockEditor({
218
218
  }
219
219
 
220
220
  try {
221
+ editor.selection = {
222
+ anchor: {
223
+ offset: 0,
224
+ path,
225
+ },
226
+ focus: {
227
+ offset: 0,
228
+ path,
229
+ },
230
+ };
231
+
221
232
  Transforms.moveNodes(editor, {
222
233
  from: [path[0]],
223
234
  match: node => {
@@ -252,7 +263,13 @@ export default function BlockEditor({
252
263
  onChange={onSlateChange}
253
264
  >
254
265
  <div>
255
- <Toolbar hover={false} onSaveClick={onSaveClick}/>
266
+ <Toolbar
267
+ hover={false}
268
+ onSaveClick={onSaveClick}
269
+ storybookStories={loadedStorybookStories}
270
+ isLoadingStories={isLoadingStories}
271
+ editor={editor}
272
+ sdk={contentfulSdk}/>
256
273
  </div>
257
274
  <div className="relative h-full max-h-full overflow-scroll px-8 py-4" ref={scrollContainer}>
258
275
  {isLoading && (
@@ -290,7 +307,7 @@ export default function BlockEditor({
290
307
  storybookElement={selectedStorybookElement}
291
308
  onChange={handleSidebarEditorChange}
292
309
  storybookStories={loadedStorybookStories}
293
- isLoading={isSidebarLoading}
310
+ isLoading={isLoadingStories}
294
311
  onClose={handleSidebarClose}
295
312
  onDelete={handleSidebarDeleteClick}
296
313
  onMove={handleSidebarMoveClick}
@@ -26,7 +26,7 @@ export const AssetList = ({
26
26
  onChange={handleChange}
27
27
  />
28
28
  <hr className="my-2" style={{borderColor: "#cfd9e0"}}/>
29
- {onAddClick && index === assets.length - 1 && <button onClick={onAddClick} className="button--secondary">Hinzufügen</button>}
29
+ {onAddClick && index === assets.length - 1 && <button onClick={onAddClick} className="button button--secondary">Hinzufügen</button>}
30
30
  </>
31
31
  ));
32
32
 
@@ -88,9 +88,11 @@ export const SidebarEditorField = ({
88
88
  return (
89
89
  <div>
90
90
  <input
91
+ id={fieldKey}
91
92
  type="checkbox"
92
93
  checked={value}
93
94
  onChange={e => onChange(fieldKey, e.target.checked)} />
95
+ <label htmlFor={fieldKey}>{value ? "Ja" : "Nein"}</label>
94
96
  </div>
95
97
  );
96
98
  case "select":
@@ -243,6 +245,7 @@ export const SidebarEditorField = ({
243
245
  <>
244
246
  {!!value && <div className="mb-2"><Asset sdk={sdk} asset={value} onDeleteClick={() => onChange(fieldKey, null)} onChange={v => onChange(fieldKey, v)} /></div>}
245
247
  <button
248
+ className="button"
246
249
  onClick={() => {
247
250
  sdk.dialogs.selectSingleEntry({contentTypes: field.control.contentTypes}).then(content => {
248
251
  onChange(fieldKey, content);
@@ -271,6 +274,7 @@ export const SidebarEditorField = ({
271
274
  </details>
272
275
  )}
273
276
  <button
277
+ className="button"
274
278
  onClick={() => {
275
279
  sdk.dialogs.selectMultipleEntries({contentTypes: field.control.contentTypes}).then(contents => {
276
280
  onChange(fieldKey, contents);
@@ -284,6 +288,7 @@ export const SidebarEditorField = ({
284
288
  <>
285
289
  {!!value && <div className="mb-2"><Asset cloudinary sdk={sdk} asset={value} onDeleteClick={() => onChange(fieldKey, null)} /></div>}
286
290
  <button
291
+ className="button"
287
292
  onClick={() => {
288
293
  sdk.dialogs.openCurrentApp({
289
294
  width: "fullWidth",
@@ -328,6 +333,7 @@ export const SidebarEditorField = ({
328
333
  </details>
329
334
  )}
330
335
  <button
336
+ className="button"
331
337
  onClick={() => {
332
338
  sdk.dialogs.openCurrentApp({
333
339
  width: "fullWidth",
@@ -384,7 +390,7 @@ export const SidebarEditorField = ({
384
390
  </div>
385
391
  );
386
392
  })}
387
- <button className="button--secondary" onClick={() => onChange(fieldKey, [...(storybookElement.attributes[fieldKey] || []), {}])}>Neue Zeile</button>
393
+ <button className="button button--secondary" onClick={() => onChange(fieldKey, [...(storybookElement.attributes[fieldKey] || []), {}])}>Neue Zeile</button>
388
394
  </div>
389
395
  </details>
390
396
  );
@@ -21,6 +21,12 @@ import {StorybookButton} from "./Element";
21
21
  import {LinkButton} from "./Link";
22
22
  import {InsertDividerButton} from "./Insert";
23
23
  import {InsertGridButton} from "./Layout";
24
+ import {
25
+ Autocomplete, Spinner,
26
+ } from "@contentful/forma-36-react-components";
27
+ import {Transforms} from "slate";
28
+
29
+ const devMode = localStorage.getItem("dev-mode") === "true";
24
30
 
25
31
  export const Portal = ({children}) => {
26
32
  return ReactDOM.createPortal(children, window.document.body);
@@ -29,9 +35,12 @@ export const Portal = ({children}) => {
29
35
  export const Toolbar = ({
30
36
  hover,
31
37
  onSaveClick,
38
+ storybookStories,
39
+ isLoadingStories,
40
+ editor,
41
+ sdk,
32
42
  }) => {
33
43
  const ref = useRef();
34
- const editor = useSlate();
35
44
 
36
45
  useEffect(() => {
37
46
  if (!hover) {
@@ -93,39 +102,50 @@ export const Toolbar = ({
93
102
  })}
94
103
  >
95
104
  <div className="toolbar-btns">
96
- <div className="flex grow">
97
- <FormatButtonBold/>
98
- <FormatButtonItalic/>
99
- <FormatButtonUnderline/>
100
- <FormatButtonStrikethrough/>
101
-
102
- <LinkButton/>
103
-
104
- <ToobarHoverExpandButton>
105
- <BlockButtonHeading level={2}/>
106
- <BlockButtonHeading level={3}/>
107
- <BlockButtonHeading level={4}/>
108
- </ToobarHoverExpandButton>
109
-
110
- <ToobarHoverExpandButton>
111
- <BlockButtonUnorderedList/>
112
- <BlockButtonOrderedList/>
113
- <BlockButtonArrowList/>
114
- </ToobarHoverExpandButton>
115
-
116
- <BlockButtonBlockquote/>
117
-
118
- <ToobarHoverExpandButton>
119
- <AlignButtonLeft/>
120
- <AlignButtonCenter/>
121
- <AlignButtonRight/>
122
- </ToobarHoverExpandButton>
123
-
124
- <ToobarHoverExpandButton>
125
- <StorybookButton/>
126
- <InsertDividerButton/>
127
- <InsertGridButton/>
128
- </ToobarHoverExpandButton>
105
+ <div className="flex grow items-center">
106
+ <div className="flex">
107
+ <FormatButtonBold/>
108
+ <FormatButtonItalic/>
109
+ <FormatButtonUnderline/>
110
+ <FormatButtonStrikethrough/>
111
+
112
+ <LinkButton/>
113
+
114
+ <ToobarHoverExpandButton>
115
+ <BlockButtonHeading level={2}/>
116
+ <BlockButtonHeading level={3}/>
117
+ <BlockButtonHeading level={4}/>
118
+ </ToobarHoverExpandButton>
119
+
120
+ <ToobarHoverExpandButton>
121
+ <BlockButtonUnorderedList/>
122
+ <BlockButtonOrderedList/>
123
+ <BlockButtonArrowList/>
124
+ </ToobarHoverExpandButton>
125
+
126
+ <BlockButtonBlockquote/>
127
+
128
+ <ToobarHoverExpandButton>
129
+ <AlignButtonLeft/>
130
+ <AlignButtonCenter/>
131
+ <AlignButtonRight/>
132
+ </ToobarHoverExpandButton>
133
+
134
+ <ToobarHoverExpandButton>
135
+ <StorybookButton/>
136
+ <InsertDividerButton/>
137
+ <InsertGridButton/>
138
+ </ToobarHoverExpandButton>
139
+ </div>
140
+
141
+ <div>
142
+ <ElementAutocomplete
143
+ isLoading={isLoadingStories}
144
+ storybookStories={storybookStories}
145
+ editor={editor}
146
+ storyContext={sdk.parameters.instance.storyContext}
147
+ />
148
+ </div>
129
149
  </div>
130
150
  {!!onSaveClick && <button className="!w-auto !border-0 !bg-green-600 !text-xs font-bold text-white hover:!bg-green-700" onClick={onSaveClick}>Änderungen speichern</button>}
131
151
  </div>
@@ -143,6 +163,84 @@ export const Toolbar = ({
143
163
  }
144
164
  };
145
165
 
166
+ const ElementAutocomplete = ({
167
+ storybookStories,
168
+ isLoading,
169
+ editor,
170
+ storyContext,
171
+ }) => {
172
+ const [filteredItems, setFilteredItems] = React.useState(items);
173
+
174
+ const items = (storybookStories || []).map(story => {
175
+ let storyTitleSplit = story.title.split("/");
176
+
177
+ if (!story.id) {
178
+ return;
179
+ }
180
+
181
+ let splitStoryContext = storyContext.split(",");
182
+ let isItemInContext = splitStoryContext.find(context => {
183
+ return Array.isArray(story.storyContext) ? story.storyContext.includes(context) : context === story.storyContext;
184
+ });
185
+
186
+ if (!devMode && !isItemInContext) {
187
+ return;
188
+ }
189
+
190
+ return {
191
+ value: story.id.toLowerCase(),
192
+ label: storyTitleSplit[storyTitleSplit.length - 1],
193
+ stories: story.stories,
194
+ };
195
+ }).filter(Boolean);
196
+
197
+ useEffect(() => {
198
+ setFilteredItems(items);
199
+ }, [storybookStories]);
200
+
201
+ const handleQueryChange = (query) => {
202
+ setFilteredItems(query ? items.filter((item) => item.label.toLowerCase().includes(query.toLowerCase())) : items);
203
+ };
204
+
205
+ const handleOnChange = (item) => {
206
+ let element = {
207
+ children: [{text: ''}],
208
+ type: "storybook",
209
+ block: item.value,
210
+ attributes: {...(item?.stories?.[0]?.args || item?.stories?.[1]?.args || {})},
211
+ };
212
+
213
+ Transforms.insertNodes(editor, [element]);
214
+ };
215
+
216
+ useEffect(() => {
217
+ let autoCompleteElement = document.getElementsByClassName("element-autocomplete")[0];
218
+
219
+ if (autoCompleteElement) {
220
+ autoCompleteElement.value = "";
221
+ }
222
+ }, []);
223
+
224
+ return (
225
+ <Autocomplete
226
+ items={filteredItems}
227
+ onQueryChange={handleQueryChange}
228
+ isLoading={isLoading}
229
+ placeholder={'Suchen...'}
230
+ emptyListMessage={'Keine Komponenten gefunden'}
231
+ noMatchesMessage={'Keine Ergebnisse gefunden'}
232
+ dropdownProps={{isFullWidth: true}}
233
+ maxHeight={300}
234
+ onChange={handleOnChange}
235
+ width="medium"
236
+ >
237
+ {(options) =>
238
+ options.map((option) => <span key={option.value}>{option.label}</span>)
239
+ }
240
+ </Autocomplete>
241
+ );
242
+ };
243
+
146
244
  export const ToobarHoverExpandButton = ({children}) => {
147
245
  return <span
148
246
  className={