@zipify/wysiwyg 2.0.0-9 → 2.1.0-builder-modes.0

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.
Files changed (39) hide show
  1. package/dist/cli.js +2 -2
  2. package/dist/wysiwyg.css +9 -9
  3. package/dist/wysiwyg.mjs +2691 -2683
  4. package/lib/assets/icons/indicator.svg +1 -1
  5. package/lib/cli/commands/ToJsonCommand.js +15 -6
  6. package/lib/components/toolbar/controls/AlignmentControl.vue +1 -1
  7. package/lib/components/toolbar/controls/FontColorControl.vue +1 -1
  8. package/lib/components/toolbar/controls/FontFamilyControl.vue +1 -1
  9. package/lib/components/toolbar/controls/FontSizeControl.vue +1 -1
  10. package/lib/components/toolbar/controls/FontWeightControl.vue +1 -1
  11. package/lib/components/toolbar/controls/ItalicControl.vue +1 -1
  12. package/lib/components/toolbar/controls/LineHeightControl.vue +1 -1
  13. package/lib/entry-lib.js +1 -1
  14. package/lib/extensions/Alignment.js +2 -5
  15. package/lib/extensions/FontSize.js +24 -6
  16. package/lib/extensions/LineHeight.js +2 -8
  17. package/lib/extensions/StylePreset.js +2 -1
  18. package/lib/extensions/Superscript.js +25 -12
  19. package/lib/extensions/__tests__/StylePreset.test.js +4 -4
  20. package/lib/extensions/__tests__/__snapshots__/Alignment.test.js.snap +2 -2
  21. package/lib/extensions/__tests__/__snapshots__/FontSize.test.js.snap +5 -5
  22. package/lib/extensions/__tests__/__snapshots__/LineHeight.test.js.snap +1 -1
  23. package/lib/extensions/__tests__/__snapshots__/Superscript.test.js.snap +5 -5
  24. package/lib/extensions/core/NodeProcessor.js +11 -7
  25. package/lib/extensions/core/index.js +16 -7
  26. package/lib/extensions/core/plugins/PastePlugin.js +4 -0
  27. package/lib/extensions/core/plugins/PlaceholderPlugin.js +30 -0
  28. package/lib/extensions/core/plugins/ProseMirrorPlugin.js +4 -3
  29. package/lib/extensions/core/plugins/index.js +1 -0
  30. package/lib/extensions/index.js +2 -1
  31. package/lib/extensions/list/List.js +7 -4
  32. package/lib/extensions/list/ListItem.js +35 -4
  33. package/lib/services/NodeFactory.js +1 -4
  34. package/lib/services/__tests__/__snapshots__/NodeFactory.test.js.snap +1 -1
  35. package/lib/styles/content.css +1 -1
  36. package/lib/utils/__tests__/isMarkAppliedToParent.test.js +5 -5
  37. package/lib/utils/isMarkAppliedToParent.js +7 -2
  38. package/package.json +9 -3
  39. package/lib/extensions/core/CopyPasteProcessor.js +0 -10
@@ -1,4 +1,4 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 9 9">
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" style="width:var(--zw-icon-width);height:var(--zw-icon-height)" viewBox="0 0 9 9">
2
2
  <path fill="#FFAB00" d="M0 4.5a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0Z"/>
3
3
  <path fill="#fff" fill-rule="evenodd" d="M5.063 2.25H3.938v3.375h1.124V2.25Zm-1.125 4.5a.562.562 0 1 1 1.123-.001.562.562 0 0 1-1.123.001Z" clip-rule="evenodd"/>
4
4
  </svg>
@@ -9,19 +9,25 @@ export class ToJsonCommand extends Command {
9
9
 
10
10
  options = [
11
11
  {
12
- flags: '--config <path>',
12
+ flags: '-c, --config <path>',
13
13
  description: 'Generator config',
14
14
  // Relative to dist folder
15
15
  default: resolve(__dirname, '../bin/zp.config.json')
16
+ },
17
+ {
18
+ flags: '-f, --format <type>',
19
+ description: 'Set output format',
20
+ default: 'rb'
16
21
  }
17
22
  ];
18
23
 
19
- doCommand(html, { config }) {
24
+ doCommand(html, { config, format }) {
20
25
  const configPath = resolve(process.cwd(), config);
21
26
  const serializer = ContentSerializer.build(require(configPath).editor);
22
- const json = serializer.toJSON(this.#formatInputHtml(html));
27
+ const content = serializer.toJSON(this.#formatInputHtml(html));
28
+ const json = this.#stringifyContent(content);
23
29
 
24
- this.output(this.#formatOutputJson(json));
30
+ this.output(format === 'rb' ? this.#formatOutputRb(json) : json);
25
31
  }
26
32
 
27
33
  #formatInputHtml(html) {
@@ -32,10 +38,13 @@ export class ToJsonCommand extends Command {
32
38
  });
33
39
  }
34
40
 
35
- #formatOutputJson(object) {
41
+ #stringifyContent(content) {
36
42
  const skipNullValue = (_, value) => value === null ? undefined : value;
37
- const json = JSON.stringify(object, skipNullValue, 2);
38
43
 
44
+ return JSON.stringify(content, skipNullValue, 2);
45
+ }
46
+
47
+ #formatOutputRb(json) {
39
48
  return json
40
49
  .replace(/\\"/g, '"')
41
50
  .replace(/font-family: ?'(.+)'/g, 'font-family: "$1"')
@@ -66,7 +66,7 @@ export default {
66
66
  const editor = inject(InjectionTokens.EDITOR);
67
67
 
68
68
  const currentValue = editor.commands.getAlignment();
69
- const isCustomized = editor.commands.isSettingCustomized('attributes', TextSettings.ALIGNMENT);
69
+ const isCustomized = editor.commands.isSettingCustomized(TextSettings.ALIGNMENT);
70
70
 
71
71
  function apply(value) {
72
72
  editor.chain().focus().applyAlignment(value).run();
@@ -48,7 +48,7 @@ export default {
48
48
  const editor = inject(InjectionTokens.EDITOR);
49
49
 
50
50
  const currentValue = editor.commands.getFontColor();
51
- const isCustomized = editor.commands.isSettingCustomized('marks', TextSettings.FONT_COLOR);
51
+ const isCustomized = editor.commands.isSettingCustomized(TextSettings.FONT_COLOR);
52
52
 
53
53
  const apply = (color) => editor.chain().applyFontColor(color).run();
54
54
 
@@ -69,7 +69,7 @@ export default {
69
69
  }
70
70
 
71
71
  const currentValue = editor.commands.getFontFamily();
72
- const isCustomized = editor.commands.isSettingCustomized('marks', TextSettings.FONT_FAMILY);
72
+ const isCustomized = editor.commands.isSettingCustomized(TextSettings.FONT_FAMILY);
73
73
 
74
74
  const apply = (fontFamily) => {
75
75
  recentFontNames.add(fontFamily);
@@ -36,7 +36,7 @@ export default {
36
36
  });
37
37
 
38
38
  const currentValue = editor.commands.getFontSize();
39
- const isCustomized = editor.commands.isSettingCustomized('marks', TextSettings.FONT_SIZE);
39
+ const isCustomized = editor.commands.isSettingCustomized(TextSettings.FONT_SIZE);
40
40
 
41
41
  const apply = (value) => editor.chain().focus().applyFontSize(value).run();
42
42
 
@@ -34,7 +34,7 @@ export default {
34
34
  const options = computed(() => unref(font).weights.map((style) => ({ id: style })));
35
35
 
36
36
  const currentValue = editor.commands.getFontWeight();
37
- const isCustomized = editor.commands.isSettingCustomized('marks', TextSettings.FONT_WEIGHT);
37
+ const isCustomized = editor.commands.isSettingCustomized(TextSettings.FONT_WEIGHT);
38
38
 
39
39
  const apply = (value) => editor.chain().focus().applyFontWeight(value).run();
40
40
 
@@ -44,7 +44,7 @@ export default {
44
44
  const editor = inject(InjectionTokens.EDITOR);
45
45
 
46
46
  const currentValue = editor.commands.isItalic();
47
- const isCustomized = editor.commands.isSettingCustomized('marks', TextSettings.FONT_STYLE);
47
+ const isCustomized = editor.commands.isSettingCustomized(TextSettings.FONT_STYLE);
48
48
 
49
49
  const isAvailable = editor.commands.isItalicAvailable();
50
50
  const apply = () => editor.chain().focus().toggleItalic().run();
@@ -79,7 +79,7 @@ export default {
79
79
  const toggler = useModalToggler({ wrapperRef, modalRef });
80
80
 
81
81
  const currentValue = editor.commands.getLineHeight();
82
- const isCustomized = editor.commands.isSettingCustomized('attributes', TextSettings.LINE_HEIGHT);
82
+ const isCustomized = editor.commands.isSettingCustomized(TextSettings.LINE_HEIGHT);
83
83
 
84
84
  const apply = (value) => editor.commands.applyLineHeight(String(value));
85
85
 
package/lib/entry-lib.js CHANGED
@@ -1,4 +1,4 @@
1
1
  export { default as Wysiwyg } from './Wysiwyg';
2
2
  export { NodeFactory } from './services';
3
3
  export { NodeTypes, TextSettings, Alignments } from './enums';
4
- export { isWysiwygContent } from './utils';
4
+ export { isWysiwygContent, unmarkWysiwygContent, markWysiwygContent } from './utils';
@@ -52,12 +52,9 @@ export const Alignment = Extension.create({
52
52
  addCommands() {
53
53
  return {
54
54
  applyAlignment: createCommand(({ commands }, value) => {
55
- // const device = unref(commands.getDevice());
56
- //
57
- // commands.setBlockAttributes(this.name, { [device]: value }, DEFAULTS);
55
+ const device = unref(commands.getDevice());
58
56
 
59
- // Temporary until release BUILDER_MODES
60
- commands.setBlockAttributes(this.name, { desktop: value, tablet: value, mobile: value });
57
+ commands.setBlockAttributes(this.name, { [device]: value }, DEFAULTS);
61
58
  }),
62
59
 
63
60
  getAlignment: createCommand(({ commands }) => {
@@ -2,6 +2,7 @@ import { Mark } from '@tiptap/vue-2';
2
2
  import { computed, unref } from 'vue';
3
3
  import { convertFontSize, createCommand, createKeyboardShortcut, renderMark } from '../utils';
4
4
  import { MarkGroups, TextSettings } from '../enums';
5
+ import { AddNodeMarkStep } from './core';
5
6
 
6
7
  export const FontSize = Mark.create({
7
8
  name: TextSettings.FONT_SIZE,
@@ -34,12 +35,31 @@ export const FontSize = Mark.create({
34
35
  }),
35
36
 
36
37
  applyFontSize: createCommand(({ commands }, value) => {
37
- // const device = unref(commands.getDevice());
38
+ const device = unref(commands.getDevice());
38
39
 
39
- // commands.applyMark(this.name, { [device]: value });
40
+ commands.applyMark(this.name, { [device]: value }, {
41
+ isAppliedToParent: (parentMark, mark) => {
42
+ if (parentMark.type.name !== mark.type.name) return false;
40
43
 
41
- // Temporary until release BUILDER_MODES
42
- commands.applyMark(this.name, { desktop: value, tablet: value, mobile: null });
44
+ return parentMark.attrs[device] === mark.attrs[device];
45
+ },
46
+
47
+ onAppliedToParent: ({ tr, node, position, mark }) => {
48
+ const attrs = { ...mark.attrs, [device]: null };
49
+ const canRemove = !Object.values(attrs).some((value) => !!value);
50
+
51
+ if (canRemove) return false;
52
+
53
+ const updated = mark.type.create(attrs);
54
+
55
+ if (node.isText) {
56
+ tr.addMark(position, position + node.nodeSize, updated);
57
+ return;
58
+ }
59
+
60
+ tr.step(new AddNodeMarkStep(position, updated));
61
+ }
62
+ });
43
63
  }),
44
64
 
45
65
  increaseFontSize: createCommand(({ commands }) => {
@@ -89,8 +109,6 @@ export const FontSize = Mark.create({
89
109
  getAttrs: (input) => {
90
110
  const value = parseSize(input);
91
111
 
92
- // return { desktop: value, tablet: value, mobile: value };
93
- // Temporary until release BUILDER_MODES
94
112
  return { desktop: value, tablet: value, mobile: null };
95
113
  }
96
114
  }
@@ -36,9 +36,6 @@ export const LineHeight = Extension.create({
36
36
  const wrapperEl = unref(this.options.wrapperRef);
37
37
  const converted = convertLineHeight(value, element, wrapperEl);
38
38
 
39
- // return { desktop: converted, tablet: converted, mobile: converted };
40
-
41
- // Temporary until release BUILDER_MODES
42
39
  return { desktop: converted, tablet: converted, mobile: null };
43
40
  },
44
41
 
@@ -75,12 +72,9 @@ export const LineHeight = Extension.create({
75
72
  }),
76
73
 
77
74
  applyLineHeight: createCommand(({ commands }, value) => {
78
- // const device = unref(commands.getDevice());
79
- //
80
- // commands.setBlockAttributes(this.name, { [device]: value }, DEFAULTS);
75
+ const device = unref(commands.getDevice());
81
76
 
82
- // Temporary until release BUILDER_MODES
83
- commands.setBlockAttributes(this.name, { desktop: value, tablet: value, mobile: null }, DEFAULTS);
77
+ commands.setBlockAttributes(this.name, { [device]: value }, DEFAULTS);
84
78
  })
85
79
  };
86
80
  }
@@ -161,8 +161,9 @@ export const StylePreset = Extension.create({
161
161
  });
162
162
  }),
163
163
 
164
- isSettingCustomized: createCommand(({ commands }, group, name) => {
164
+ isSettingCustomized: createCommand(({ commands }, name) => {
165
165
  const customization = commands.getPresetCustomization();
166
+ const group = TextSettings.attributes.includes(name) ? 'attributes' : 'marks';
166
167
 
167
168
  return computed(() => unref(customization)[group]?.includes(name) ?? false);
168
169
  }),
@@ -1,27 +1,25 @@
1
- import Base from '@tiptap/extension-superscript';
1
+ import { Mark } from '@tiptap/vue-2';
2
2
  import { computed, unref } from 'vue';
3
3
  import { createCommand } from '../utils';
4
4
  import { TextSettings } from '../enums';
5
5
 
6
- export const Superscript = Base.extend({
6
+ export const Superscript = Mark.create({
7
7
  name: TextSettings.SUPERSCRIPT,
8
- addKeyboardShortcuts: null,
9
-
10
- addOptions: () => ({
11
- HTMLAttributes: { class: 'zw-superscript' }
12
- }),
13
8
 
14
9
  addCommands() {
15
- const { setSuperscript, unsetSuperscript } = this.parent();
16
-
17
10
  return {
18
- applySuperscript: setSuperscript,
19
- removeSuperscript: unsetSuperscript,
11
+ applySuperscript: createCommand(({ commands }) => {
12
+ commands.setMark(this.name);
13
+ }),
14
+
15
+ removeSuperscript: createCommand(({ commands }) => {
16
+ commands.unsetMark(this.name);
17
+ }),
20
18
 
21
19
  toggleSuperscript: createCommand(({ commands }) => {
22
20
  const isActive = unref(commands.isSuperscript());
23
21
 
24
- isActive ? commands.applySuperscript() : commands.removeSuperscript();
22
+ isActive ? commands.removeSuperscript() : commands.applySuperscript();
25
23
  }),
26
24
 
27
25
  isSuperscript: createCommand(({ commands }) => {
@@ -30,5 +28,20 @@ export const Superscript = Base.extend({
30
28
  return computed(() => !!unref(selectionRef));
31
29
  })
32
30
  };
31
+ },
32
+
33
+ parseHTML() {
34
+ return [
35
+ { tag: 'sup' },
36
+
37
+ {
38
+ style: 'vertical-align',
39
+ getAttrs: (value) => value !== 'super' ? false : null
40
+ }
41
+ ];
42
+ },
43
+
44
+ renderHTML() {
45
+ return ['sup', { class: 'zw-superscript' }, 0];
33
46
  }
34
47
  });
@@ -413,8 +413,8 @@ describe('get preset customization', () => {
413
413
  presets: [createPreset({ id: 'regular-1' })]
414
414
  });
415
415
 
416
- const isFontSizeCustomized = editor.commands.isSettingCustomized('marks', TextSettings.FONT_SIZE);
417
- const isAlignmentCustomized = editor.commands.isSettingCustomized('attributes', TextSettings.ALIGNMENT);
416
+ const isFontSizeCustomized = editor.commands.isSettingCustomized(TextSettings.FONT_SIZE);
417
+ const isAlignmentCustomized = editor.commands.isSettingCustomized(TextSettings.ALIGNMENT);
418
418
 
419
419
  expect(isFontSizeCustomized.value).toBe(false);
420
420
  expect(isAlignmentCustomized.value).toBe(false);
@@ -454,7 +454,7 @@ describe('get preset customization', () => {
454
454
  });
455
455
 
456
456
  editor.commands.selectAll();
457
- const isFontSizeCustomized = editor.commands.isSettingCustomized('marks', TextSettings.FONT_SIZE);
457
+ const isFontSizeCustomized = editor.commands.isSettingCustomized(TextSettings.FONT_SIZE);
458
458
 
459
459
  expect(isFontSizeCustomized.value).toBe(true);
460
460
  });
@@ -489,7 +489,7 @@ describe('get preset customization', () => {
489
489
  });
490
490
 
491
491
  editor.commands.selectAll();
492
- const isAlignmentCustomized = editor.commands.isSettingCustomized('attributes', TextSettings.ALIGNMENT);
492
+ const isAlignmentCustomized = editor.commands.isSettingCustomized(TextSettings.ALIGNMENT);
493
493
 
494
494
  expect(isAlignmentCustomized.value).toBe(true);
495
495
  });
@@ -7,8 +7,8 @@ Object {
7
7
  "attrs": Object {
8
8
  "alignment": Object {
9
9
  "desktop": "right",
10
- "mobile": "right",
11
- "tablet": "right",
10
+ "mobile": null,
11
+ "tablet": null,
12
12
  },
13
13
  },
14
14
  "content": Array [
@@ -15,7 +15,7 @@ Object {
15
15
  "attrs": Object {
16
16
  "desktop": "16",
17
17
  "mobile": null,
18
- "tablet": "16",
18
+ "tablet": null,
19
19
  },
20
20
  "type": "font_size",
21
21
  },
@@ -42,7 +42,7 @@ Object {
42
42
  "attrs": Object {
43
43
  "desktop": "13",
44
44
  "mobile": null,
45
- "tablet": "13",
45
+ "tablet": null,
46
46
  },
47
47
  "type": "font_size",
48
48
  },
@@ -69,7 +69,7 @@ Object {
69
69
  "attrs": Object {
70
70
  "desktop": "15",
71
71
  "mobile": null,
72
- "tablet": "15",
72
+ "tablet": null,
73
73
  },
74
74
  "type": "font_size",
75
75
  },
@@ -96,7 +96,7 @@ Object {
96
96
  "attrs": Object {
97
97
  "desktop": "5",
98
98
  "mobile": null,
99
- "tablet": "5",
99
+ "tablet": null,
100
100
  },
101
101
  "type": "font_size",
102
102
  },
@@ -123,7 +123,7 @@ Object {
123
123
  "attrs": Object {
124
124
  "desktop": "20",
125
125
  "mobile": null,
126
- "tablet": "20",
126
+ "tablet": null,
127
127
  },
128
128
  "type": "font_size",
129
129
  },
@@ -8,7 +8,7 @@ Object {
8
8
  "line_height": Object {
9
9
  "desktop": "1.41",
10
10
  "mobile": null,
11
- "tablet": "1.41",
11
+ "tablet": null,
12
12
  },
13
13
  },
14
14
  "content": Array [
@@ -45,6 +45,11 @@ Object {
45
45
  Object {
46
46
  "content": Array [
47
47
  Object {
48
+ "marks": Array [
49
+ Object {
50
+ "type": "superscript",
51
+ },
52
+ ],
48
53
  "text": "hello world",
49
54
  "type": "text",
50
55
  },
@@ -62,11 +67,6 @@ Object {
62
67
  Object {
63
68
  "content": Array [
64
69
  Object {
65
- "marks": Array [
66
- Object {
67
- "type": "superscript",
68
- },
69
- ],
70
70
  "text": "hello world",
71
71
  "type": "text",
72
72
  },
@@ -37,7 +37,7 @@ export const NodeProcessor = Extension.create({
37
37
  return Object.keys(attrs).length ? attrs : null;
38
38
  })),
39
39
 
40
- applyMark: createCommand(({ state, commands }, name, value) => {
40
+ applyMark: createCommand(({ state, commands }, name, value, customizer = {}) => {
41
41
  const { tr, doc, schema } = state;
42
42
  const { $from, $to } = tr.selection;
43
43
  const markType = getMarkType(name, schema);
@@ -50,6 +50,14 @@ export const NodeProcessor = Extension.create({
50
50
 
51
51
  if ($from.pos === $to.pos) return;
52
52
 
53
+ const onAppliedToParent = (context) => {
54
+ if (!customizer.onAppliedToParent || customizer.onAppliedToParent(context) === false) {
55
+ const { tr, node, position, mark } = context;
56
+
57
+ commands._removeNodeMark({ tr, node, position, mark: mark.type });
58
+ }
59
+ };
60
+
53
61
  doc.nodesBetween($from.pos, $to.pos, (node, position) => {
54
62
  if (node.type.name === NodeTypes.LIST) return;
55
63
 
@@ -57,8 +65,8 @@ export const NodeProcessor = Extension.create({
57
65
  const applyingMark = markType.create({ ...(initialMark?.attrs || {}), ...value });
58
66
  const textPosition = resolveTextPosition($from, $to, node, position);
59
67
 
60
- if (isMarkAppliedToParent(tr.doc, position, applyingMark)) {
61
- return commands._removeNodeMark({ tr, node, position, mark: markType });
68
+ if (isMarkAppliedToParent(tr.doc, position, applyingMark, customizer.isAppliedToParent)) {
69
+ return onAppliedToParent({ tr, node, position, mark: applyingMark });
62
70
  }
63
71
 
64
72
  if (node.isText) {
@@ -141,10 +149,6 @@ export const NodeProcessor = Extension.create({
141
149
  });
142
150
  }),
143
151
 
144
- removeMark: createCommand(({ commands, state }, node, position, mark) => {
145
- commands._removeNodeMark({ tr: state.tr, node, position, mark });
146
- }),
147
-
148
152
  _removeNodeMark: createCommand((context, { tr, node, position, mark }) => {
149
153
  return node.isText
150
154
  ? tr.removeMark(position, position + node.nodeSize, mark)
@@ -1,20 +1,27 @@
1
+ import { Extension } from '@tiptap/vue-2';
1
2
  import Text from '@tiptap/extension-text';
2
- import Placeholder from '@tiptap/extension-placeholder';
3
3
  import History from '@tiptap/extension-history';
4
4
  import { NodeProcessor } from './NodeProcessor';
5
5
  import { TextProcessor } from './TextProcessor';
6
6
  import { SelectionProcessor } from './SelectionProcessor';
7
- import { CopyPasteProcessor } from './CopyPasteProcessor';
8
7
  import { Document } from './Document';
9
8
  import { Paragraph } from './Paragraph';
10
9
  import { Heading } from './Heading';
10
+ import { PastePlugin, PlaceholderPlugin } from './plugins';
11
+
12
+ const ProseMirrorPlugins = Extension.create({
13
+ name: 'prose_mirror_plugins',
14
+
15
+ addProseMirrorPlugins() {
16
+ return [
17
+ PastePlugin.create(this.editor),
18
+ PlaceholderPlugin.create(this.editor)
19
+ ];
20
+ }
21
+ });
11
22
 
12
23
  export const buildCoreExtensions = () => [
13
24
  Document,
14
- Placeholder.configure({
15
- placeholder: 'Type your text here...',
16
- emptyNodeClass: 'zw-wysiwyg__placeholder'
17
- }),
18
25
  Paragraph,
19
26
  Heading,
20
27
  Text,
@@ -22,5 +29,7 @@ export const buildCoreExtensions = () => [
22
29
  NodeProcessor,
23
30
  TextProcessor,
24
31
  SelectionProcessor,
25
- CopyPasteProcessor
32
+ ProseMirrorPlugins
26
33
  ];
34
+
35
+ export * from './steps';
@@ -11,6 +11,10 @@ export class PastePlugin extends ProseMirrorPlugin {
11
11
  }
12
12
 
13
13
  _transformPastedHTML(html) {
14
+ if (html.includes('data-pm-slice') && html.includes('zw-style')) {
15
+ return html;
16
+ }
17
+
14
18
  const normalizer = ContentNormalizer.build(html);
15
19
 
16
20
  normalizer.normalizeHTML();
@@ -0,0 +1,30 @@
1
+ import { Decoration, DecorationSet } from 'prosemirror-view';
2
+ import { ProseMirrorPlugin } from './ProseMirrorPlugin';
3
+
4
+ export class PlaceholderPlugin extends ProseMirrorPlugin {
5
+ buildProps() {
6
+ return { decorations: this.#buildDecorations.bind(this) };
7
+ }
8
+
9
+ #buildDecorations({ doc }) {
10
+ const decorations = [];
11
+
12
+ if (!this.editor.isEditable) return null;
13
+ if (doc.childCount > 1) return;
14
+
15
+ doc.descendants((node, pos) => {
16
+ if (!node.childCount) {
17
+ const decoration = Decoration.node(pos, pos + node.nodeSize, {
18
+ class: 'zw-wysiwyg__placeholder',
19
+ 'data-placeholder': 'Type your text here...'
20
+ });
21
+
22
+ decorations.push(decoration);
23
+ }
24
+
25
+ return false;
26
+ });
27
+
28
+ return DecorationSet.create(doc, decorations);
29
+ }
30
+ }
@@ -1,8 +1,8 @@
1
1
  import { Plugin, PluginKey } from 'prosemirror-state';
2
2
 
3
3
  export class ProseMirrorPlugin {
4
- static create(options) {
5
- const plugin = new this(options || {});
4
+ static create(editor, options) {
5
+ const plugin = new this(editor, options || {});
6
6
 
7
7
  return new Plugin({
8
8
  key: new PluginKey(this.name),
@@ -10,8 +10,9 @@ export class ProseMirrorPlugin {
10
10
  });
11
11
  }
12
12
 
13
- constructor(options) {
13
+ constructor(editor, options) {
14
14
  this.options = options;
15
+ this.editor = editor;
15
16
  }
16
17
 
17
18
  buildProps() {
@@ -1 +1,2 @@
1
1
  export { PastePlugin } from './PastePlugin';
2
+ export { PlaceholderPlugin } from './PlaceholderPlugin';
@@ -34,7 +34,8 @@ export function buildExtensions(options) {
34
34
  makeVariable: options.makePresetVariable
35
35
  }),
36
36
  List.configure({
37
- baseClass: options.baseListClass
37
+ baseClass: options.baseListClass,
38
+ presetClass: options.basePresetClass + options.defaultPresetId
38
39
  }),
39
40
  DeviceManager.configure({
40
41
  device: options.deviceRef
@@ -16,7 +16,8 @@ export const List = Node.create({
16
16
  ],
17
17
 
18
18
  addOptions: () => ({
19
- baseClass: ''
19
+ baseClass: '',
20
+ presetClass: ''
20
21
  }),
21
22
 
22
23
  addAttributes: () => ({
@@ -60,11 +61,13 @@ export const List = Node.create({
60
61
  },
61
62
 
62
63
  renderHTML({ HTMLAttributes: attrs }) {
64
+ const classes = [
65
+ this.options.baseClass + attrs.bullet.type,
66
+ this.options.presetClass
67
+ ];
63
68
  const isOrdered = ListTypes.ordered.includes(attrs.bullet.type);
64
- const tag = isOrdered ? 'ol' : 'ul';
65
- const bulletClass = this.options.baseClass + attrs.bullet.type;
66
69
 
67
- return [tag, { class: bulletClass }, 0];
70
+ return [isOrdered ? 'ol' : 'ul', { class: classes.join(' ') }, 0];
68
71
  },
69
72
 
70
73
  addCommands() {