@zipify/wysiwyg 2.0.0-7 → 2.0.0-9

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 (41) hide show
  1. package/config/build/cli.config.js +8 -2
  2. package/dist/cli.js +3 -3
  3. package/dist/wysiwyg.css +41 -31
  4. package/dist/wysiwyg.mjs +198 -47
  5. package/lib/Wysiwyg.vue +3 -2
  6. package/lib/assets/icons/indicator.svg +4 -0
  7. package/lib/cli/commands/Command.js +39 -0
  8. package/lib/cli/commands/ToJsonCommand.js +46 -0
  9. package/lib/cli/commands/VersionCommand.js +11 -0
  10. package/lib/cli/commands/index.js +2 -0
  11. package/lib/cli/index.js +1 -0
  12. package/lib/components/base/Button.vue +6 -0
  13. package/lib/components/base/dropdown/Dropdown.vue +7 -1
  14. package/lib/components/base/dropdown/DropdownActivator.vue +25 -4
  15. package/lib/components/base/dropdown/__tests__/DropdownActivator.test.js +23 -1
  16. package/lib/components/toolbar/controls/AlignmentControl.vue +12 -1
  17. package/lib/components/toolbar/controls/FontColorControl.vue +14 -0
  18. package/lib/components/toolbar/controls/FontFamilyControl.vue +4 -0
  19. package/lib/components/toolbar/controls/FontSizeControl.vue +6 -1
  20. package/lib/components/toolbar/controls/FontWeightControl.vue +12 -0
  21. package/lib/components/toolbar/controls/ItalicControl.vue +14 -0
  22. package/lib/components/toolbar/controls/LineHeightControl.vue +15 -0
  23. package/lib/components/toolbar/controls/UnderlineControl.vue +13 -0
  24. package/lib/components/toolbar/controls/__tests__/AlignmentControl.test.js +72 -5
  25. package/lib/components/toolbar/controls/__tests__/FontColorControl.test.js +22 -1
  26. package/lib/components/toolbar/controls/__tests__/FontFamilyControl.test.js +1 -0
  27. package/lib/components/toolbar/controls/__tests__/FontSizeControl.test.js +1 -0
  28. package/lib/components/toolbar/controls/__tests__/FontWeightControl.test.js +1 -0
  29. package/lib/components/toolbar/controls/__tests__/ItalicControl.test.js +23 -1
  30. package/lib/components/toolbar/controls/__tests__/LineHeightControl.test.js +23 -1
  31. package/lib/components/toolbar/controls/__tests__/UnderlineControl.test.js +25 -1
  32. package/lib/composables/__tests__/useEditor.test.js +1 -1
  33. package/lib/composables/useEditor.js +9 -8
  34. package/lib/directives/__tests__/tooltip.test.js +22 -4
  35. package/lib/directives/tooltip.js +4 -1
  36. package/lib/entry-cli.js +7 -21
  37. package/lib/extensions/StylePreset.js +6 -0
  38. package/lib/extensions/TextDecoration.js +7 -0
  39. package/lib/extensions/__tests__/StylePreset.test.js +51 -0
  40. package/lib/extensions/__tests__/TextDecoration.test.js +20 -0
  41. package/package.json +4 -2
@@ -0,0 +1,39 @@
1
+ export class Command {
2
+ name;
3
+ description;
4
+ argument;
5
+ options = [];
6
+
7
+ doCommand() {
8
+ throw new Error('Command "doCommand" is required');
9
+ }
10
+
11
+ install(program) {
12
+ if (!this.name) {
13
+ throw new Error('Command "name" is required');
14
+ }
15
+
16
+ let building = program.command(this.name);
17
+
18
+ if (this.description) {
19
+ building = building.description(this.description);
20
+ }
21
+
22
+ if (this.argument) {
23
+ building = building.argument(this.argument);
24
+ }
25
+
26
+ if (this.options.length) {
27
+ for (const option of this.options) {
28
+ building = building.option(option.flags, option.description, option.default);
29
+ }
30
+ }
31
+
32
+ building.action(this.doCommand.bind(this));
33
+ }
34
+
35
+ output(data) {
36
+ // eslint-disable-next-line no-console
37
+ console.log(data);
38
+ }
39
+ }
@@ -0,0 +1,46 @@
1
+ import { resolve } from 'path';
2
+ import { ContentSerializer } from '../ContentSerializer';
3
+ import { Command } from './Command';
4
+
5
+ export class ToJsonCommand extends Command {
6
+ name = 'to-json';
7
+ description = 'migrate html to json';
8
+ argument = '<html>';
9
+
10
+ options = [
11
+ {
12
+ flags: '--config <path>',
13
+ description: 'Generator config',
14
+ // Relative to dist folder
15
+ default: resolve(__dirname, '../bin/zp.config.json')
16
+ }
17
+ ];
18
+
19
+ doCommand(html, { config }) {
20
+ const configPath = resolve(process.cwd(), config);
21
+ const serializer = ContentSerializer.build(require(configPath).editor);
22
+ const json = serializer.toJSON(this.#formatInputHtml(html));
23
+
24
+ this.output(this.#formatOutputJson(json));
25
+ }
26
+
27
+ #formatInputHtml(html) {
28
+ return html
29
+ .replace(/\\(["'])/g, '$1')
30
+ .replace(/rgba\(\d{1,3}, ?\d{1,3}, ?\d{1,3}, (\d{1,2}%)\)/g, (substring, alpha) => {
31
+ return substring.replace(alpha, parseFloat(alpha) / 100);
32
+ });
33
+ }
34
+
35
+ #formatOutputJson(object) {
36
+ const skipNullValue = (_, value) => value === null ? undefined : value;
37
+ const json = JSON.stringify(object, skipNullValue, 2);
38
+
39
+ return json
40
+ .replace(/\\"/g, '"')
41
+ .replace(/font-family: ?'(.+)'/g, 'font-family: "$1"')
42
+ .replace(/'/g, '\\\'')
43
+ .replace(/^[\t ]*"[^:\n\r]+(?<!\\)":/gm, (match) => match.replace(/"/g, ''))
44
+ .replace(/: "(.+)"([,\n])/g, ': \'$1\'$2');
45
+ }
46
+ }
@@ -0,0 +1,11 @@
1
+ import { version } from '../../../package.json';
2
+ import { Command } from './Command';
3
+
4
+ export class VersionCommand extends Command {
5
+ name = 'version';
6
+ description = 'display cli version';
7
+
8
+ doCommand() {
9
+ this.output(version);
10
+ }
11
+ }
@@ -0,0 +1,2 @@
1
+ export { ToJsonCommand } from './ToJsonCommand';
2
+ export { VersionCommand } from './VersionCommand';
package/lib/cli/index.js CHANGED
@@ -1 +1,2 @@
1
1
  export { ContentSerializer } from './ContentSerializer';
2
+ export * from './commands';
@@ -135,4 +135,10 @@ export default {
135
135
  color: rgb(var(--zw-color-white));
136
136
  background-color: rgb(var(--zw-color-n5));
137
137
  }
138
+
139
+ .zw-button__customized-indicator {
140
+ position: absolute;
141
+ top: 0;
142
+ right: 2px;
143
+ }
138
144
  </style>
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div class="zw-dropdown" ref="dropdownRef">
3
- <DropdownActivator :color="color">
3
+ <DropdownActivator :color="color" :is-customized="isCustomized">
4
4
  <template #default="attrs">
5
5
  <slot name="activator" v-bind="attrs" />
6
6
  </template>
@@ -60,6 +60,12 @@ export default {
60
60
  type: String,
61
61
  required: false,
62
62
  default: 'none'
63
+ },
64
+
65
+ isCustomized: {
66
+ type: Boolean,
67
+ required: false,
68
+ default: false
63
69
  }
64
70
  },
65
71
 
@@ -12,6 +12,15 @@
12
12
  {{ activeOptionTitle }}
13
13
  </span>
14
14
 
15
+ <Icon
16
+ v-if="isCustomized"
17
+ class="zw-dropdown__customized-indicator"
18
+ name="indicator"
19
+ size="9px"
20
+ data-test-selector="customizedIndicator"
21
+ v-tooltip.xs="'Default parameter setting is changed'"
22
+ />
23
+
15
24
  <Icon
16
25
  class="zw-dropdown__activator-arrow"
17
26
  name="arrow"
@@ -27,6 +36,7 @@
27
36
  import { computed, inject, toRef } from 'vue';
28
37
  import Button from '../Button';
29
38
  import Icon from '../Icon';
39
+ import { tooltip } from '../../../directives';
30
40
  import { InjectionTokens } from './injectionTokens';
31
41
  import { useDropdownEntityTitle } from './composables';
32
42
 
@@ -38,6 +48,10 @@ export default {
38
48
  Button
39
49
  },
40
50
 
51
+ directives: {
52
+ tooltip
53
+ },
54
+
41
55
  model: {
42
56
  event: 'change'
43
57
  },
@@ -47,6 +61,12 @@ export default {
47
61
  type: String,
48
62
  required: false,
49
63
  default: 'none'
64
+ },
65
+
66
+ isCustomized: {
67
+ type: Boolean,
68
+ required: false,
69
+ default: false
50
70
  }
51
71
  },
52
72
 
@@ -77,10 +97,6 @@ export default {
77
97
  width: 100%;
78
98
  }
79
99
 
80
- .zw-dropdown__activator-title {
81
- margin-right: var(--zw-offset-xs);
82
- }
83
-
84
100
  .zw-dropdown__activator-arrow {
85
101
  margin-left: auto;
86
102
  }
@@ -94,4 +110,9 @@ export default {
94
110
  font-size: var(--zw-font-size-xxs);
95
111
  color: rgb(var(--zw-color-white));
96
112
  }
113
+
114
+ .zw-dropdown__customized-indicator {
115
+ position: relative;
116
+ top: calc(0px - var(--zw-offset-xs));
117
+ }
97
118
  </style>
@@ -4,6 +4,10 @@ import DropdownActivator from '../DropdownActivator';
4
4
  import { InjectionTokens } from '../injectionTokens';
5
5
  import Button from '../../Button';
6
6
 
7
+ const SELECTORS = {
8
+ INDICATOR: '[data-test-selector="customizedIndicator"]'
9
+ };
10
+
7
11
  const createToggler = ({ isOpened } = {}) => ({
8
12
  isOpened: ref(isOpened ?? false),
9
13
  open: jest.fn()
@@ -13,8 +17,12 @@ const createActiveOptionManager = ({ activeOption } = {}) => ({
13
17
  activeOption: ref(activeOption ?? { id: 'test' })
14
18
  });
15
19
 
16
- function createComponent({ toggler, activeOptionManager } = {}) {
20
+ function createComponent({ toggler, isCustomized, activeOptionManager } = {}) {
17
21
  return shallowMount(DropdownActivator, {
22
+ propsData: {
23
+ isCustomized: ref(isCustomized ?? false)
24
+ },
25
+
18
26
  provide: {
19
27
  [InjectionTokens.TOGGLER]: toggler ?? createToggler(),
20
28
  [InjectionTokens.ACTIVE_MANAGER]: activeOptionManager ?? createActiveOptionManager()
@@ -40,6 +48,20 @@ describe('rendering', () => {
40
48
  expect(buttonWrapper.props('active')).toBe(false);
41
49
  expect(buttonWrapper.classes('zw-dropdown__activator--active')).toBeFalsy();
42
50
  });
51
+
52
+ test('should render indicator of customized styles', () => {
53
+ const toggler = createToggler({ isOpened: false });
54
+ const wrapper = createComponent({ toggler, isCustomized: true });
55
+
56
+ expect(wrapper).toVueContainComponent(SELECTORS.INDICATOR);
57
+ });
58
+
59
+ test('should not render indicator of customized styles', () => {
60
+ const toggler = createToggler({ isOpened: false });
61
+ const wrapper = createComponent({ toggler });
62
+
63
+ expect(wrapper).not.toVueContainComponent(SELECTORS.INDICATOR);
64
+ });
43
65
  });
44
66
 
45
67
  describe('open dropdown', () => {
@@ -2,12 +2,21 @@
2
2
  <ButtonToggle :value="currentValue" :options="$options.alignments" @change="apply">
3
3
  <template #option="{ isActive, option, activate }">
4
4
  <Button
5
+ class="zw-position--relative"
5
6
  icon
6
7
  skin="toolbar"
7
8
  :active="isActive"
8
9
  @click="activate"
9
10
  v-tooltip="option.tooltip"
10
11
  >
12
+ <Icon
13
+ v-if="isCustomized && isActive"
14
+ class="zw-button__customized-indicator"
15
+ name="indicator"
16
+ size="9px"
17
+ data-test-selector="customizedIndicator"
18
+ v-tooltip.xs="'Default parameter setting is changed'"
19
+ />
11
20
  <Icon :name="`alignment-${option.id}`" size="28px" auto-color />
12
21
  </Button>
13
22
  </template>
@@ -17,7 +26,7 @@
17
26
  <script>
18
27
  import { inject } from 'vue';
19
28
  import { InjectionTokens } from '../../../injectionTokens';
20
- import { Alignments } from '../../../enums';
29
+ import { Alignments, TextSettings } from '../../../enums';
21
30
  import { ButtonToggle, Button, Icon } from '../../base';
22
31
  import { tooltip } from '../../../directives';
23
32
 
@@ -57,6 +66,7 @@ export default {
57
66
  const editor = inject(InjectionTokens.EDITOR);
58
67
 
59
68
  const currentValue = editor.commands.getAlignment();
69
+ const isCustomized = editor.commands.isSettingCustomized('attributes', TextSettings.ALIGNMENT);
60
70
 
61
71
  function apply(value) {
62
72
  editor.chain().focus().applyAlignment(value).run();
@@ -65,6 +75,7 @@ export default {
65
75
 
66
76
  return {
67
77
  currentValue,
78
+ isCustomized,
68
79
  apply
69
80
  };
70
81
  }
@@ -2,6 +2,7 @@
2
2
  <ColorPicker :value="currentValue" @change="apply">
3
3
  <template #activator="{ toggle, isOpened }">
4
4
  <Button
5
+ class="zw-position--relative"
5
6
  icon
6
7
  skin="toolbar"
7
8
  :active="isOpened"
@@ -9,6 +10,15 @@
9
10
  v-tooltip="'Font Color'"
10
11
  >
11
12
  <Icon name="font-color" size="28px" auto-color />
13
+
14
+ <Icon
15
+ v-if="isCustomized"
16
+ class="zw-button__customized-indicator"
17
+ name="indicator"
18
+ size="9px"
19
+ data-test-selector="customizedIndicator"
20
+ v-tooltip.xs="'Default parameter setting is changed'"
21
+ />
12
22
  </Button>
13
23
  </template>
14
24
  </ColorPicker>
@@ -19,6 +29,7 @@ import { inject } from 'vue';
19
29
  import { ColorPicker, Button, Icon } from '../../base';
20
30
  import { InjectionTokens } from '../../../injectionTokens';
21
31
  import { tooltip } from '../../../directives';
32
+ import { TextSettings } from '../../../enums';
22
33
 
23
34
  export default {
24
35
  name: 'FontColorControl',
@@ -37,10 +48,13 @@ export default {
37
48
  const editor = inject(InjectionTokens.EDITOR);
38
49
 
39
50
  const currentValue = editor.commands.getFontColor();
51
+ const isCustomized = editor.commands.isSettingCustomized('marks', TextSettings.FONT_COLOR);
52
+
40
53
  const apply = (color) => editor.chain().applyFontColor(color).run();
41
54
 
42
55
  return {
43
56
  currentValue,
57
+ isCustomized,
44
58
  apply
45
59
  };
46
60
  }
@@ -3,6 +3,7 @@
3
3
  class="zw-font-family-control"
4
4
  :options="options"
5
5
  :value="currentValue"
6
+ :is-customized="isCustomized"
6
7
  @change="apply"
7
8
  v-tooltip="'Font Name'"
8
9
  >
@@ -22,6 +23,7 @@ import { computed, inject } from 'vue';
22
23
  import { InjectionTokens } from '../../../injectionTokens';
23
24
  import { tooltip } from '../../../directives';
24
25
  import { Dropdown, DropdownOption } from '../../base';
26
+ import { TextSettings } from '../../../enums';
25
27
  import { useRecentFonts } from './composables';
26
28
 
27
29
  export default {
@@ -67,6 +69,7 @@ export default {
67
69
  }
68
70
 
69
71
  const currentValue = editor.commands.getFontFamily();
72
+ const isCustomized = editor.commands.isSettingCustomized('marks', TextSettings.FONT_FAMILY);
70
73
 
71
74
  const apply = (fontFamily) => {
72
75
  recentFontNames.add(fontFamily);
@@ -76,6 +79,7 @@ export default {
76
79
  return {
77
80
  options,
78
81
  currentValue,
82
+ isCustomized,
79
83
  renderOptionStyles,
80
84
  apply
81
85
  };
@@ -3,6 +3,7 @@
3
3
  class="zw-font-size-control"
4
4
  :options="options"
5
5
  :value="currentValue"
6
+ :is-customized="isCustomized"
6
7
  @change="apply"
7
8
  v-tooltip="{ text: 'Font Size', hotkey: 'Mod Shift +/-' }"
8
9
  />
@@ -13,6 +14,7 @@ import { computed, inject } from 'vue';
13
14
  import { Dropdown } from '../../base';
14
15
  import { InjectionTokens } from '../../../injectionTokens';
15
16
  import { tooltip } from '../../../directives';
17
+ import { TextSettings } from '../../../enums';
16
18
 
17
19
  export default {
18
20
  name: 'FontSizeControl',
@@ -34,11 +36,14 @@ export default {
34
36
  });
35
37
 
36
38
  const currentValue = editor.commands.getFontSize();
39
+ const isCustomized = editor.commands.isSettingCustomized('marks', TextSettings.FONT_SIZE);
40
+
37
41
  const apply = (value) => editor.chain().focus().applyFontSize(value).run();
38
42
 
39
43
  return {
40
44
  options,
41
45
  currentValue,
46
+ isCustomized,
42
47
  apply
43
48
  };
44
49
  }
@@ -47,6 +52,6 @@ export default {
47
52
 
48
53
  <style scoped>
49
54
  .zw-font-size-control {
50
- width: 64px;
55
+ width: 72px;
51
56
  }
52
57
  </style>
@@ -1,7 +1,9 @@
1
1
  <template>
2
2
  <Dropdown
3
+ class="zw-font-weight-control"
3
4
  :options="options"
4
5
  :value="currentValue"
6
+ :is-customized="isCustomized"
5
7
  @change="apply"
6
8
  v-tooltip="{ text: 'Font Weight', hotkey: 'Mod B' }"
7
9
  />
@@ -12,6 +14,7 @@ import { computed, inject, unref } from 'vue';
12
14
  import { InjectionTokens } from '../../../injectionTokens';
13
15
  import { Dropdown } from '../../base';
14
16
  import { tooltip } from '../../../directives';
17
+ import { TextSettings } from '../../../enums';
15
18
 
16
19
  export default {
17
20
  name: 'FontWeightControl',
@@ -29,15 +32,24 @@ export default {
29
32
  const font = editor.commands.getFont();
30
33
 
31
34
  const options = computed(() => unref(font).weights.map((style) => ({ id: style })));
35
+
32
36
  const currentValue = editor.commands.getFontWeight();
37
+ const isCustomized = editor.commands.isSettingCustomized('marks', TextSettings.FONT_WEIGHT);
33
38
 
34
39
  const apply = (value) => editor.chain().focus().applyFontWeight(value).run();
35
40
 
36
41
  return {
37
42
  options,
38
43
  currentValue,
44
+ isCustomized,
39
45
  apply
40
46
  };
41
47
  }
42
48
  };
43
49
  </script>
50
+
51
+ <style scoped>
52
+ .zw-font-weight-control {
53
+ width: 60px;
54
+ }
55
+ </style>
@@ -1,5 +1,6 @@
1
1
  <template>
2
2
  <Button
3
+ class="zw-position--relative"
3
4
  skin="toolbar"
4
5
  icon
5
6
  :active="currentValue"
@@ -8,6 +9,15 @@
8
9
  v-tooltip="{ text: 'Italic', hotkey: 'Mod I' }"
9
10
  >
10
11
  <Icon name="italic" size="28px" auto-color />
12
+
13
+ <Icon
14
+ v-if="isCustomized"
15
+ class="zw-button__customized-indicator"
16
+ name="indicator"
17
+ size="9px"
18
+ data-test-selector="customizedIndicator"
19
+ v-tooltip.xs="'Default parameter setting is changed'"
20
+ />
11
21
  </Button>
12
22
  </template>
13
23
 
@@ -16,6 +26,7 @@ import { inject } from 'vue';
16
26
  import { Button, Icon } from '../../base';
17
27
  import { InjectionTokens } from '../../../injectionTokens';
18
28
  import { tooltip } from '../../../directives';
29
+ import { TextSettings } from '../../../enums';
19
30
 
20
31
  export default {
21
32
  name: 'ItalicControl',
@@ -33,12 +44,15 @@ export default {
33
44
  const editor = inject(InjectionTokens.EDITOR);
34
45
 
35
46
  const currentValue = editor.commands.isItalic();
47
+ const isCustomized = editor.commands.isSettingCustomized('marks', TextSettings.FONT_STYLE);
48
+
36
49
  const isAvailable = editor.commands.isItalicAvailable();
37
50
  const apply = () => editor.chain().focus().toggleItalic().run();
38
51
 
39
52
  return {
40
53
  isAvailable,
41
54
  currentValue,
55
+ isCustomized,
42
56
  apply
43
57
  };
44
58
  }
@@ -1,6 +1,7 @@
1
1
  <template>
2
2
  <div class="zw-position--relative" ref="wrapperRef">
3
3
  <Button
4
+ class="zw-position--relative"
4
5
  icon
5
6
  skin="toolbar"
6
7
  :active="isOpened"
@@ -8,6 +9,15 @@
8
9
  v-tooltip="'Line Height'"
9
10
  >
10
11
  <Icon name="line-height" size="28px" auto-color />
12
+
13
+ <Icon
14
+ v-if="isCustomized"
15
+ class="zw-button__customized-indicator"
16
+ name="indicator"
17
+ size="9px"
18
+ data-test-selector="customizedIndicator"
19
+ v-tooltip.xs="'Default parameter setting is changed'"
20
+ />
11
21
  </Button>
12
22
 
13
23
  <Modal class="zw-line-height-control__modal" ref="modalRef" :toggler="toggler">
@@ -44,6 +54,7 @@ import { inject, ref } from 'vue';
44
54
  import { Button, Icon, Modal, Range, NumberField, FieldLabel, useModalToggler } from '../../base';
45
55
  import { InjectionTokens } from '../../../injectionTokens';
46
56
  import { tooltip } from '../../../directives';
57
+ import { TextSettings } from '../../../enums';
47
58
 
48
59
  export default {
49
60
  name: 'LineHeightControl',
@@ -66,13 +77,17 @@ export default {
66
77
  const modalRef = ref(null);
67
78
  const editor = inject(InjectionTokens.EDITOR);
68
79
  const toggler = useModalToggler({ wrapperRef, modalRef });
80
+
69
81
  const currentValue = editor.commands.getLineHeight();
82
+ const isCustomized = editor.commands.isSettingCustomized('attributes', TextSettings.LINE_HEIGHT);
83
+
70
84
  const apply = (value) => editor.commands.applyLineHeight(String(value));
71
85
 
72
86
  return {
73
87
  wrapperRef,
74
88
  modalRef,
75
89
  isOpened: toggler.isOpened,
90
+ isCustomized,
76
91
  toggler,
77
92
  currentValue,
78
93
  apply
@@ -1,5 +1,6 @@
1
1
  <template>
2
2
  <Button
3
+ class="zw-position--relative"
3
4
  skin="toolbar"
4
5
  icon
5
6
  :active="currentValue"
@@ -7,6 +8,15 @@
7
8
  v-tooltip="{ text: 'Underline', hotkey: 'Mod U' }"
8
9
  >
9
10
  <Icon name="underline" size="28px" auto-color />
11
+
12
+ <Icon
13
+ v-if="isCustomized"
14
+ class="zw-button__customized-indicator"
15
+ name="indicator"
16
+ size="9px"
17
+ data-test-selector="customizedIndicator"
18
+ v-tooltip.xs="'Default parameter setting is changed'"
19
+ />
10
20
  </Button>
11
21
  </template>
12
22
 
@@ -32,10 +42,13 @@ export default {
32
42
  const editor = inject(InjectionTokens.EDITOR);
33
43
 
34
44
  const currentValue = editor.commands.isUnderline();
45
+ const isCustomized = editor.commands.isUnderlineCustomized();
46
+
35
47
  const apply = () => editor.chain().focus().toggleUnderline().run();
36
48
 
37
49
  return {
38
50
  currentValue,
51
+ isCustomized,
39
52
  apply
40
53
  };
41
54
  }