@zipify/wysiwyg 1.0.0-dev.97 → 1.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.
package/dist/wysiwyg.css CHANGED
@@ -754,6 +754,13 @@ $font-height--md: 1.72;
754
754
  font-style: var(--zw-font-style, var(--zw-preset-font-style));
755
755
  text-decoration: var(--zw-text-decoration, var(--zw-preset-text-decoration));
756
756
  background-color: var(--zw-background-color, var(--zw-preset-background-color));
757
+ margin: var(--zw-margin);
758
+ }
759
+ h1.zw-style.zw-style.zw-style,
760
+ h2.zw-style.zw-style.zw-style,
761
+ h3.zw-style.zw-style.zw-style,
762
+ h4.zw-style.zw-style.zw-style {
763
+ margin: var(--zw-margin, 8px 0);
757
764
  }
758
765
  @media (min-width: 1200px) {
759
766
  .zw-style.zw-style.zw-style {
package/dist/wysiwyg.mjs CHANGED
@@ -13971,10 +13971,12 @@ const TextSettings = Object.freeze({
13971
13971
  LINE_HEIGHT: "line_height",
13972
13972
  TEXT_DECORATION: "text_decoration",
13973
13973
  SUPERSCRIPT: "superscript",
13974
+ MARGIN: "margin",
13974
13975
  get attributes() {
13975
13976
  return [
13976
13977
  this.ALIGNMENT,
13977
- this.LINE_HEIGHT
13978
+ this.LINE_HEIGHT,
13979
+ this.MARGIN
13978
13980
  ];
13979
13981
  },
13980
13982
  get marks() {
@@ -19438,42 +19440,54 @@ class FavoriteColors {
19438
19440
  }
19439
19441
  }
19440
19442
  const _ContentNormalizer = class {
19443
+ static build(content) {
19444
+ return new _ContentNormalizer({
19445
+ parser: _ContentNormalizer.PARSER,
19446
+ content
19447
+ });
19448
+ }
19441
19449
  static normalize(content) {
19442
- const options = { content, parser: _ContentNormalizer.PARSER };
19443
- return new _ContentNormalizer(options).normalize();
19450
+ return _ContentNormalizer.build(content).normalize();
19444
19451
  }
19445
19452
  constructor({ content, parser }) {
19446
19453
  this._content = content;
19447
19454
  this._parser = parser;
19448
- this._dom = null;
19455
+ this.dom = null;
19449
19456
  }
19450
19457
  normalize() {
19451
19458
  if (typeof this._content !== "string") {
19452
19459
  return this._content;
19453
19460
  }
19454
- return this._normalizeTextContent();
19461
+ this.normalizeHTML();
19462
+ return this.normalizedHTML;
19455
19463
  }
19456
- _normalizeTextContent() {
19457
- this._dom = this._parser.parseFromString(this._content.replace(/(\r)?\n/g, ""), "text/html");
19464
+ normalizeHTML() {
19465
+ this.dom = this._parser.parseFromString(this._content.replace(/(\r)?\n/g, ""), "text/html");
19458
19466
  this._removeComments();
19459
- this._iterateNodes(this._normalizeBreakLines, (node) => node.tagName === "BR");
19460
- this._iterateNodes(this._removeEmptyNodes, this._isBlockNode);
19461
- this._iterateNodes(this._normalizeListItems, (node) => node.tagName === "LI");
19462
- this._iterateNodes(this._normalizeSettingsStructure, (node) => node.tagName === "SPAN");
19463
- this._iterateNodes(this._normalizeStyles, (node) => node.tagName !== "SPAN");
19464
- return this._dom.body.innerHTML;
19467
+ this.iterateNodes(this._normalizeBreakLines, (node) => node.tagName === "BR");
19468
+ this.iterateNodes(this._removeEmptyNodes, this._isBlockNode);
19469
+ this.iterateNodes(this._normalizeListItems, (node) => node.tagName === "LI");
19470
+ this.iterateNodes(this._normalizeSettingsStructure, (node) => node.tagName === "SPAN");
19471
+ this.iterateNodes(this._normalizeStyles, (node) => node.tagName !== "SPAN");
19472
+ }
19473
+ get normalizedHTML() {
19474
+ return this.dom.body.innerHTML;
19465
19475
  }
19466
19476
  _removeComments() {
19467
- const iterator = this._dom.createNodeIterator(this._dom.body, NodeFilter.SHOW_COMMENT);
19468
- this._runIterator(iterator, (node) => node.remove());
19477
+ const iterator = this.createNodeIterator(NodeFilter.SHOW_COMMENT);
19478
+ this.runIterator(iterator, (node) => node.remove());
19479
+ }
19480
+ createNodeIterator(whatToShow, filter2) {
19481
+ return this.dom.createNodeIterator(this.dom.body, whatToShow, filter2);
19469
19482
  }
19470
- _iterateNodes(handler, condition = () => true) {
19471
- const iterator = this._dom.createNodeIterator(this._dom.body, NodeFilter.SHOW_ELEMENT, {
19472
- acceptNode: (node) => condition.call(this, node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT
19483
+ iterateNodes(handler, condition = () => true) {
19484
+ const checkCondition = (node) => node.tagName !== "BODY" && condition.call(this, node);
19485
+ const iterator = this.createNodeIterator(NodeFilter.SHOW_ELEMENT, {
19486
+ acceptNode: (node) => checkCondition(node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT
19473
19487
  });
19474
- this._runIterator(iterator, handler);
19488
+ this.runIterator(iterator, handler);
19475
19489
  }
19476
- _runIterator(iterator, handler) {
19490
+ runIterator(iterator, handler) {
19477
19491
  let currentNode = iterator.nextNode();
19478
19492
  while (currentNode) {
19479
19493
  handler.call(this, currentNode);
@@ -19481,8 +19495,7 @@ const _ContentNormalizer = class {
19481
19495
  }
19482
19496
  }
19483
19497
  _removeEmptyNodes(node) {
19484
- const html2 = node.innerHTML.replace(/ /g, "").trim();
19485
- if (!html2)
19498
+ if (!node.innerHTML.trim())
19486
19499
  node.remove();
19487
19500
  }
19488
19501
  _normalizeListItems(itemEl) {
@@ -19617,8 +19630,16 @@ const _ContentNormalizer = class {
19617
19630
  };
19618
19631
  let ContentNormalizer = _ContentNormalizer;
19619
19632
  __publicField(ContentNormalizer, "PARSER", new DOMParser());
19620
- __publicField(ContentNormalizer, "BLOCK_STYLES", ["text-align", "line-height"]);
19621
19633
  __publicField(ContentNormalizer, "BLOCK_NODE_NAMES", ["P", "H1", "H2", "H3", "H4"]);
19634
+ __publicField(ContentNormalizer, "BLOCK_STYLES", [
19635
+ "text-align",
19636
+ "line-height",
19637
+ "margin",
19638
+ "margin-top",
19639
+ "margin-bottom",
19640
+ "margin-left",
19641
+ "margin-right"
19642
+ ]);
19622
19643
  __publicField(ContentNormalizer, "ASSIGN_STYLE_RULES", [
19623
19644
  {
19624
19645
  tag: /^(b|strong)$/,
@@ -25690,10 +25711,26 @@ class ProseMirrorPlugin {
25690
25711
  class PastePlugin extends ProseMirrorPlugin {
25691
25712
  buildProps() {
25692
25713
  return {
25693
- transformPastedHTML: ContentNormalizer.normalize,
25714
+ transformPastedHTML: this._transformPastedHTML.bind(this),
25694
25715
  handlePaste: this._handlePaste.bind(this)
25695
25716
  };
25696
25717
  }
25718
+ _transformPastedHTML(html2) {
25719
+ const normalizer = ContentNormalizer.build(html2);
25720
+ normalizer.normalizeHTML();
25721
+ this._removeDeprecatedStyles(normalizer);
25722
+ return normalizer.normalizedHTML;
25723
+ }
25724
+ _removeDeprecatedStyles(normalizer) {
25725
+ const elements = normalizer.dom.querySelectorAll('[style*="margin"]');
25726
+ for (const element of Array.from(elements)) {
25727
+ element.style.removeProperty("margin");
25728
+ element.style.removeProperty("margin-top");
25729
+ element.style.removeProperty("margin-right");
25730
+ element.style.removeProperty("margin-bottom");
25731
+ element.style.removeProperty("margin-left");
25732
+ }
25733
+ }
25697
25734
  _handlePaste(view, _, slice2) {
25698
25735
  const transaction = this._insertPastedContent(view, slice2).scrollIntoView().setMeta("paste", true).setMeta("uiEvent", "paste");
25699
25736
  view.dispatch(transaction);
@@ -25748,6 +25785,34 @@ const buildCoreExtensions = () => [
25748
25785
  SelectionProcessor,
25749
25786
  CopyPasteProcessor
25750
25787
  ];
25788
+ const Margin = Extension.create({
25789
+ name: TextSettings.MARGIN,
25790
+ addGlobalAttributes: () => [
25791
+ {
25792
+ types: [NodeTypes.PARAGRAPH, NodeTypes.HEADING],
25793
+ attributes: {
25794
+ [TextSettings.MARGIN]: {
25795
+ isRequired: false,
25796
+ parseHTML(el) {
25797
+ const { margin, marginTop, marginRight, marginBottom, marginLeft } = el.style;
25798
+ const isPreset = [margin, marginTop, marginRight, marginBottom, marginLeft].some((v) => !!v);
25799
+ if (!isPreset)
25800
+ return null;
25801
+ if (margin)
25802
+ return { value: margin };
25803
+ const value = [marginTop || 0, marginRight || 0, marginBottom || 0, marginLeft || 0].join(" ");
25804
+ return { value };
25805
+ },
25806
+ renderHTML(attrs) {
25807
+ if (!attrs.margin)
25808
+ return null;
25809
+ return renderInlineSetting({ margin: attrs.margin.value });
25810
+ }
25811
+ }
25812
+ }
25813
+ }
25814
+ ]
25815
+ });
25751
25816
  function buildExtensions(options) {
25752
25817
  const getPresetById = (id2) => options.presetsRef.value.find((preset) => preset.id === id2);
25753
25818
  const defaultPreset = getPresetById(options.defaultPresetId);
@@ -25765,16 +25830,16 @@ function buildExtensions(options) {
25765
25830
  DeviceManager.configure({
25766
25831
  device: options.deviceRef
25767
25832
  }),
25768
- FontFamily.configure({
25769
- fonts: options.fonts,
25770
- defaultFont: defaultPreset.common.font_family
25771
- }),
25772
- FontWeight,
25773
25833
  FontSize.configure({
25774
25834
  minSize: options.minFontSize,
25775
25835
  maxSize: options.maxFontSize,
25776
25836
  wrapperRef: options.wrapperRef
25777
25837
  }),
25838
+ FontFamily.configure({
25839
+ fonts: options.fonts,
25840
+ defaultFont: defaultPreset.common.font_family
25841
+ }),
25842
+ FontWeight,
25778
25843
  FontColor,
25779
25844
  BackgroundColor,
25780
25845
  FontStyle,
@@ -25789,7 +25854,8 @@ function buildExtensions(options) {
25789
25854
  preset: linkPreset,
25790
25855
  basePresetClass: options.basePresetClass,
25791
25856
  pageBlocks: options.pageBlocksRef
25792
- })
25857
+ }),
25858
+ Margin
25793
25859
  ]);
25794
25860
  }
25795
25861
  class Font {
@@ -9,11 +9,13 @@ export const TextSettings = Object.freeze({
9
9
  LINE_HEIGHT: 'line_height',
10
10
  TEXT_DECORATION: 'text_decoration',
11
11
  SUPERSCRIPT: 'superscript',
12
+ MARGIN: 'margin',
12
13
 
13
14
  get attributes() {
14
15
  return [
15
16
  this.ALIGNMENT,
16
- this.LINE_HEIGHT
17
+ this.LINE_HEIGHT,
18
+ this.MARGIN
17
19
  ];
18
20
  },
19
21
 
@@ -0,0 +1,38 @@
1
+ import { Extension } from '@tiptap/vue-2';
2
+ import { NodeTypes, TextSettings } from '../enums';
3
+ import { renderInlineSetting } from '../utils';
4
+
5
+ // Fallback margins in old wysiwyg content
6
+ export const Margin = Extension.create({
7
+ name: TextSettings.MARGIN,
8
+
9
+ addGlobalAttributes: () => [
10
+ {
11
+ types: [NodeTypes.PARAGRAPH, NodeTypes.HEADING],
12
+
13
+ attributes: {
14
+ [TextSettings.MARGIN]: {
15
+ isRequired: false,
16
+
17
+ parseHTML(el) {
18
+ const { margin, marginTop, marginRight, marginBottom, marginLeft } = el.style;
19
+ const isPreset = [margin, marginTop, marginRight, marginBottom, marginLeft].some((v) => !!v);
20
+
21
+ if (!isPreset) return null;
22
+ if (margin) return { value: margin };
23
+
24
+ const value = [marginTop || 0, marginRight || 0, marginBottom || 0, marginLeft || 0].join(' ');
25
+
26
+ return { value };
27
+ },
28
+
29
+ renderHTML(attrs) {
30
+ if (!attrs.margin) return null;
31
+
32
+ return renderInlineSetting({ margin: attrs.margin.value });
33
+ }
34
+ }
35
+ }
36
+ }
37
+ ]
38
+ });
@@ -0,0 +1,59 @@
1
+ import { Editor } from '@tiptap/core';
2
+ import { ContentNormalizer, NodeFactory } from '../../services';
3
+ import { buildCoreExtensions } from '../core';
4
+ import { Margin } from '../Margin';
5
+
6
+ function createEditor({ content }) {
7
+ return new Editor({
8
+ content: ContentNormalizer.normalize(content),
9
+ extensions: buildCoreExtensions().concat(Margin)
10
+ });
11
+ }
12
+
13
+ describe('parse html', () => {
14
+ test('should set null if no margin', () => {
15
+ const editor = createEditor({
16
+ content: '<p>lorem ipsum</p>'
17
+ });
18
+
19
+ expect(editor.getJSON()).toMatchSnapshot();
20
+ });
21
+
22
+ test('should parse shorthand', () => {
23
+ const editor = createEditor({
24
+ content: '<p style="margin: 10px 3px 1em;">lorem ipsum</p>'
25
+ });
26
+
27
+ expect(editor.getJSON()).toMatchSnapshot();
28
+ });
29
+
30
+ test('should parse values', () => {
31
+ const editor = createEditor({
32
+ content: '<p style="margin-left: 10px; margin-bottom: 1em">lorem ipsum</p>'
33
+ });
34
+
35
+ expect(editor.getJSON()).toMatchSnapshot();
36
+ });
37
+ });
38
+
39
+ describe('render html', () => {
40
+ test('should not render empty', () => {
41
+ const editor = createEditor({
42
+ content: NodeFactory.doc([
43
+ NodeFactory.paragraph({ margin: null }, 'lorem ipsum')
44
+ ])
45
+ });
46
+
47
+ expect(editor.getHTML()).toMatchSnapshot();
48
+ });
49
+
50
+ test('should render value', () => {
51
+ const editor = createEditor({
52
+ content: NodeFactory.doc([
53
+ NodeFactory.paragraph({ margin: { value: '10px' } }, 'lorem ipsum')
54
+ ])
55
+ });
56
+
57
+ expect(editor.getHTML()).toMatchSnapshot();
58
+ });
59
+ });
@@ -0,0 +1,69 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`parse html should parse shorthand 1`] = `
4
+ Object {
5
+ "content": Array [
6
+ Object {
7
+ "attrs": Object {
8
+ "margin": Object {
9
+ "value": "10px 3px 1em",
10
+ },
11
+ },
12
+ "content": Array [
13
+ Object {
14
+ "text": "lorem ipsum",
15
+ "type": "text",
16
+ },
17
+ ],
18
+ "type": "paragraph",
19
+ },
20
+ ],
21
+ "type": "doc",
22
+ }
23
+ `;
24
+
25
+ exports[`parse html should parse values 1`] = `
26
+ Object {
27
+ "content": Array [
28
+ Object {
29
+ "attrs": Object {
30
+ "margin": Object {
31
+ "value": "0 0 1em 10px",
32
+ },
33
+ },
34
+ "content": Array [
35
+ Object {
36
+ "text": "lorem ipsum",
37
+ "type": "text",
38
+ },
39
+ ],
40
+ "type": "paragraph",
41
+ },
42
+ ],
43
+ "type": "doc",
44
+ }
45
+ `;
46
+
47
+ exports[`parse html should set null if no margin 1`] = `
48
+ Object {
49
+ "content": Array [
50
+ Object {
51
+ "attrs": Object {
52
+ "margin": null,
53
+ },
54
+ "content": Array [
55
+ Object {
56
+ "text": "lorem ipsum",
57
+ "type": "text",
58
+ },
59
+ ],
60
+ "type": "paragraph",
61
+ },
62
+ ],
63
+ "type": "doc",
64
+ }
65
+ `;
66
+
67
+ exports[`render html should not render empty 1`] = `"<p class=\\"zw-style\\">lorem ipsum</p>"`;
68
+
69
+ exports[`render html should render value 1`] = `"<p class=\\"zw-style\\" style=\\"--zw-margin:10px;\\">lorem ipsum</p>"`;
@@ -5,11 +5,32 @@ import { ProseMirrorPlugin } from './ProseMirrorPlugin';
5
5
  export class PastePlugin extends ProseMirrorPlugin {
6
6
  buildProps() {
7
7
  return {
8
- transformPastedHTML: ContentNormalizer.normalize,
8
+ transformPastedHTML: this._transformPastedHTML.bind(this),
9
9
  handlePaste: this._handlePaste.bind(this)
10
10
  };
11
11
  }
12
12
 
13
+ _transformPastedHTML(html) {
14
+ const normalizer = ContentNormalizer.build(html);
15
+
16
+ normalizer.normalizeHTML();
17
+ this._removeDeprecatedStyles(normalizer);
18
+
19
+ return normalizer.normalizedHTML;
20
+ }
21
+
22
+ _removeDeprecatedStyles(normalizer) {
23
+ const elements = normalizer.dom.querySelectorAll('[style*="margin"]');
24
+
25
+ for (const element of Array.from(elements)) {
26
+ element.style.removeProperty('margin');
27
+ element.style.removeProperty('margin-top');
28
+ element.style.removeProperty('margin-right');
29
+ element.style.removeProperty('margin-bottom');
30
+ element.style.removeProperty('margin-left');
31
+ }
32
+ }
33
+
13
34
  _handlePaste(view, _, slice) {
14
35
  const transaction = this._insertPastedContent(view, slice)
15
36
  .scrollIntoView()
@@ -14,6 +14,7 @@ import { List } from './list';
14
14
  import { Link } from './Link';
15
15
  import { Superscript } from './Superscript';
16
16
  import { buildCoreExtensions } from './core';
17
+ import { Margin } from './Margin';
17
18
 
18
19
  export function buildExtensions(options) {
19
20
  const getPresetById = (id) => options.presetsRef.value.find((preset) => preset.id === id);
@@ -33,16 +34,17 @@ export function buildExtensions(options) {
33
34
  DeviceManager.configure({
34
35
  device: options.deviceRef
35
36
  }),
36
- FontFamily.configure({
37
- fonts: options.fonts,
38
- defaultFont: defaultPreset.common.font_family
39
- }),
40
- FontWeight,
37
+ // Should be first setting. Fix size of wrappers in headings
41
38
  FontSize.configure({
42
39
  minSize: options.minFontSize,
43
40
  maxSize: options.maxFontSize,
44
41
  wrapperRef: options.wrapperRef
45
42
  }),
43
+ FontFamily.configure({
44
+ fonts: options.fonts,
45
+ defaultFont: defaultPreset.common.font_family
46
+ }),
47
+ FontWeight,
46
48
  FontColor,
47
49
  BackgroundColor,
48
50
  FontStyle,
@@ -57,6 +59,7 @@ export function buildExtensions(options) {
57
59
  preset: linkPreset,
58
60
  basePresetClass: options.basePresetClass,
59
61
  pageBlocks: options.pageBlocksRef
60
- })
62
+ }),
63
+ Margin
61
64
  ]);
62
65
  }
@@ -1,8 +1,17 @@
1
1
  export class ContentNormalizer {
2
2
  static PARSER = new DOMParser();
3
- static BLOCK_STYLES = ['text-align', 'line-height'];
4
3
  static BLOCK_NODE_NAMES = ['P', 'H1', 'H2', 'H3', 'H4'];
5
4
 
5
+ static BLOCK_STYLES = [
6
+ 'text-align',
7
+ 'line-height',
8
+ 'margin',
9
+ 'margin-top',
10
+ 'margin-bottom',
11
+ 'margin-left',
12
+ 'margin-right'
13
+ ];
14
+
6
15
  static ASSIGN_STYLE_RULES = [
7
16
  {
8
17
  tag: /^(b|strong)$/,
@@ -18,53 +27,67 @@ export class ContentNormalizer {
18
27
  }
19
28
  ];
20
29
 
21
- static normalize(content) {
22
- const options = { content, parser: ContentNormalizer.PARSER };
30
+ static build(content) {
31
+ return new ContentNormalizer({
32
+ parser: ContentNormalizer.PARSER,
33
+ content
34
+ });
35
+ }
23
36
 
24
- return new ContentNormalizer(options).normalize();
37
+ static normalize(content) {
38
+ return ContentNormalizer.build(content).normalize();
25
39
  }
26
40
 
27
41
  constructor({ content, parser }) {
28
42
  this._content = content;
29
43
  this._parser = parser;
30
- this._dom = null;
44
+ this.dom = null;
31
45
  }
32
46
 
33
47
  normalize() {
34
48
  if (typeof this._content !== 'string') {
35
49
  return this._content;
36
50
  }
37
- return this._normalizeTextContent();
51
+ this.normalizeHTML();
52
+ return this.normalizedHTML;
38
53
  }
39
54
 
40
- _normalizeTextContent() {
41
- this._dom = this._parser.parseFromString(this._content.replace(/(\r)?\n/g, ''), 'text/html');
55
+ normalizeHTML() {
56
+ this.dom = this._parser.parseFromString(this._content.replace(/(\r)?\n/g, ''), 'text/html');
42
57
 
43
58
  this._removeComments();
44
- this._iterateNodes(this._normalizeBreakLines, (node) => node.tagName === 'BR');
45
- this._iterateNodes(this._removeEmptyNodes, this._isBlockNode);
46
- this._iterateNodes(this._normalizeListItems, (node) => node.tagName === 'LI');
47
- this._iterateNodes(this._normalizeSettingsStructure, (node) => node.tagName === 'SPAN');
48
- this._iterateNodes(this._normalizeStyles, (node) => node.tagName !== 'SPAN');
59
+ this.iterateNodes(this._normalizeBreakLines, (node) => node.tagName === 'BR');
60
+ this.iterateNodes(this._removeEmptyNodes, this._isBlockNode);
61
+ this.iterateNodes(this._normalizeListItems, (node) => node.tagName === 'LI');
62
+ this.iterateNodes(this._normalizeSettingsStructure, (node) => node.tagName === 'SPAN');
63
+ this.iterateNodes(this._normalizeStyles, (node) => node.tagName !== 'SPAN');
64
+ }
49
65
 
50
- return this._dom.body.innerHTML;
66
+ get normalizedHTML() {
67
+ return this.dom.body.innerHTML;
51
68
  }
52
69
 
53
70
  _removeComments() {
54
- const iterator = this._dom.createNodeIterator(this._dom.body, NodeFilter.SHOW_COMMENT);
71
+ const iterator = this.createNodeIterator(NodeFilter.SHOW_COMMENT);
72
+
73
+ this.runIterator(iterator, (node) => node.remove());
74
+ }
55
75
 
56
- this._runIterator(iterator, (node) => node.remove());
76
+ createNodeIterator(whatToShow, filter) {
77
+ return this.dom.createNodeIterator(this.dom.body, whatToShow, filter);
57
78
  }
58
79
 
59
- _iterateNodes(handler, condition = () => true) {
60
- const iterator = this._dom.createNodeIterator(this._dom.body, NodeFilter.SHOW_ELEMENT, {
61
- acceptNode: (node) => condition.call(this, node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT
80
+ iterateNodes(handler, condition = () => true) {
81
+ const checkCondition = (node) => node.tagName !== 'BODY' && condition.call(this, node);
82
+
83
+ const iterator = this.createNodeIterator(NodeFilter.SHOW_ELEMENT, {
84
+ acceptNode: (node) => checkCondition(node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT
62
85
  });
63
86
 
64
- this._runIterator(iterator, handler);
87
+ this.runIterator(iterator, handler);
65
88
  }
66
89
 
67
- _runIterator(iterator, handler) {
90
+ runIterator(iterator, handler) {
68
91
  let currentNode = iterator.nextNode();
69
92
 
70
93
  while (currentNode) {
@@ -74,9 +97,7 @@ export class ContentNormalizer {
74
97
  }
75
98
 
76
99
  _removeEmptyNodes(node) {
77
- const html = node.innerHTML.replace(/&nbsp;/g, '').trim();
78
-
79
- if (!html) node.remove();
100
+ if (!node.innerHTML.trim()) node.remove();
80
101
  }
81
102
 
82
103
  _normalizeListItems(itemEl) {
@@ -92,13 +92,6 @@ describe('normalize text content', () => {
92
92
  expect(ContentNormalizer.normalize(input)).toBe(output);
93
93
  });
94
94
 
95
- test('should ignore non-breaking space only nodes', () => {
96
- const input = '<p>lorem ipsum 1</p><p>&nbsp;</p><p>lorem ipsum 2</p>';
97
- const output = '<p>lorem ipsum 1</p><p>lorem ipsum 2</p>';
98
-
99
- expect(ContentNormalizer.normalize(input)).toBe(output);
100
- });
101
-
102
95
  test('should ignore newline chapters only nodes', () => {
103
96
  const input = '<p>lorem ipsum 1</p><p>\n</p><p>lorem ipsum 2</p>';
104
97
  const output = '<p>lorem ipsum 1</p><p>lorem ipsum 2</p>';
@@ -20,6 +20,14 @@
20
20
  font-style: var(--zw-font-style, var(--zw-preset-font-style));
21
21
  text-decoration: var(--zw-text-decoration, var(--zw-preset-text-decoration));
22
22
  background-color: var(--zw-background-color, var(--zw-preset-background-color));
23
+ margin: var(--zw-margin);
24
+ }
25
+
26
+ h1.zw-style.zw-style.zw-style,
27
+ h2.zw-style.zw-style.zw-style,
28
+ h3.zw-style.zw-style.zw-style,
29
+ h4.zw-style.zw-style.zw-style {
30
+ margin: var(--zw-margin, 8px 0);
23
31
  }
24
32
 
25
33
  @media (min-width: 1200px) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zipify/wysiwyg",
3
- "version": "1.0.0-dev.97",
3
+ "version": "1.0.0",
4
4
  "description": "Zipify modification of TipTap text editor",
5
5
  "main": "dist/wysiwyg.mjs",
6
6
  "repository": {