@zipify/wysiwyg 2.0.0-9 → 2.0.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 (29) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/wysiwyg.css +9 -9
  3. package/dist/wysiwyg.mjs +191 -213
  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/StylePreset.js +2 -1
  15. package/lib/extensions/Superscript.js +25 -12
  16. package/lib/extensions/__tests__/StylePreset.test.js +4 -4
  17. package/lib/extensions/__tests__/__snapshots__/Superscript.test.js.snap +5 -5
  18. package/lib/extensions/core/NodeProcessor.js +0 -4
  19. package/lib/extensions/core/index.js +14 -7
  20. package/lib/extensions/core/plugins/PastePlugin.js +4 -0
  21. package/lib/extensions/core/plugins/PlaceholderPlugin.js +30 -0
  22. package/lib/extensions/core/plugins/ProseMirrorPlugin.js +4 -3
  23. package/lib/extensions/core/plugins/index.js +1 -0
  24. package/lib/extensions/index.js +2 -1
  25. package/lib/extensions/list/List.js +7 -4
  26. package/lib/extensions/list/ListItem.js +35 -4
  27. package/lib/styles/content.css +1 -1
  28. package/package.json +9 -3
  29. package/lib/extensions/core/CopyPasteProcessor.js +0 -10
@@ -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';
@@ -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
  });
@@ -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
  },
@@ -141,10 +141,6 @@ export const NodeProcessor = Extension.create({
141
141
  });
142
142
  }),
143
143
 
144
- removeMark: createCommand(({ commands, state }, node, position, mark) => {
145
- commands._removeNodeMark({ tr: state.tr, node, position, mark });
146
- }),
147
-
148
144
  _removeNodeMark: createCommand((context, { tr, node, position, mark }) => {
149
145
  return node.isText
150
146
  ? 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,5 @@ export const buildCoreExtensions = () => [
22
29
  NodeProcessor,
23
30
  TextProcessor,
24
31
  SelectionProcessor,
25
- CopyPasteProcessor
32
+ ProseMirrorPlugins
26
33
  ];
@@ -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() {
@@ -1,17 +1,48 @@
1
1
  import Base from '@tiptap/extension-list-item';
2
2
  import { MarkGroups, NodeTypes } from '../../enums';
3
+ import { copyMark, createCommand } from '../../utils';
4
+ import { AddNodeMarkStep } from '../core/steps';
3
5
 
4
6
  export const ListItem = Base.extend({
5
7
  name: NodeTypes.LIST_ITEM,
6
8
  marks: MarkGroups.SETTINGS,
7
9
 
10
+ addCommands() {
11
+ const getItemPosition = ({ selection }) => selection.$cursor.before(selection.$cursor.depth - 1);
12
+
13
+ return {
14
+ listItemNewline: createCommand(({ commands, tr }) => {
15
+ const initialNode = tr.doc.nodeAt(getItemPosition(tr));
16
+ const isEmptyParagraph = !tr.selection.$cursor.node().textContent;
17
+
18
+ if (isEmptyParagraph) return false;
19
+
20
+ commands.splitListItem(this.name);
21
+
22
+ const position = getItemPosition(tr);
23
+
24
+ for (const mark of initialNode.marks) {
25
+ tr.step(new AddNodeMarkStep(position, copyMark(mark)));
26
+ }
27
+
28
+ return true;
29
+ })
30
+ };
31
+ },
32
+
8
33
  addOptions: () => ({
9
34
  HTMLAttributes: { class: 'zw-style' }
10
35
  }),
11
36
 
12
- addKeyboardShortcuts() {
13
- const { Enter } = this.parent();
37
+ addKeyboardShortcuts: () => ({
38
+ Enter: (context) => {
39
+ const { state, commands } = context.editor;
40
+
41
+ const isListSelected = state.selection.$cursor?.path.some((node) => {
42
+ return node.type?.name === NodeTypes.LIST;
43
+ });
14
44
 
15
- return { Enter };
16
- }
45
+ if (isListSelected) return commands.listItemNewline();
46
+ }
47
+ })
17
48
  });
@@ -1,4 +1,4 @@
1
- .zw-wysiwyg__placeholder:first-child:last-child::before {
1
+ .zw-wysiwyg__placeholder::before {
2
2
  content: attr(data-placeholder);
3
3
  color: rgb(var(--zw-color-n70));
4
4
  float: left;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zipify/wysiwyg",
3
- "version": "2.0.0-9",
3
+ "version": "2.0.0",
4
4
  "description": "Zipify modification of TipTap text editor",
5
5
  "main": "dist/wysiwyg.mjs",
6
6
  "bin": {
@@ -41,8 +41,6 @@
41
41
  "@tiptap/extension-link": "^2.0.0-beta.195",
42
42
  "@tiptap/extension-list-item": "^2.0.0-beta.195",
43
43
  "@tiptap/extension-paragraph": "^2.0.0-beta.195",
44
- "@tiptap/extension-placeholder": "^2.0.0-beta.195",
45
- "@tiptap/extension-superscript": "^2.0.0-beta.195",
46
44
  "@tiptap/extension-text": "^2.0.0-beta.195",
47
45
  "@tiptap/vue-2": "^2.0.0-beta.195",
48
46
  "commander": "^9.4.0",
@@ -54,6 +52,14 @@
54
52
  "@zipify/colorpicker": "^2.2",
55
53
  "vue": "^2.7"
56
54
  },
55
+ "peerDependenciesMeta": {
56
+ "@zipify/colorpicker": {
57
+ "optional": true
58
+ },
59
+ "vue": {
60
+ "optional": true
61
+ }
62
+ },
57
63
  "devDependencies": {
58
64
  "@babel/core": "^7.19.1",
59
65
  "@babel/eslint-parser": "^7.19.1",
@@ -1,10 +0,0 @@
1
- import { Extension } from '@tiptap/vue-2';
2
- import { PastePlugin } from './plugins';
3
-
4
- export const CopyPasteProcessor = Extension.create({
5
- name: 'copy_paste_processor',
6
-
7
- addProseMirrorPlugins() {
8
- return [PastePlugin.create()];
9
- }
10
- });