@zipify/wysiwyg 4.1.0-0 → 4.1.0-2

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 (32) hide show
  1. package/config/build/settings.js +1 -1
  2. package/config/jest/setupTests.js +3 -0
  3. package/dist/cli.js +2 -3
  4. package/dist/wysiwyg.css +37 -37
  5. package/dist/wysiwyg.mjs +920 -846
  6. package/example/ai-component/AiComponent.vue +16 -50
  7. package/lib/Wysiwyg.vue +9 -17
  8. package/lib/components/base/Modal.vue +19 -18
  9. package/lib/components/base/ModalFloating.vue +32 -0
  10. package/lib/components/base/__tests__/Modal.test.js +2 -19
  11. package/lib/components/base/composables/useModalToggler.js +3 -34
  12. package/lib/components/base/dropdown/Dropdown.vue +7 -6
  13. package/lib/components/base/dropdown/DropdownActivator.vue +4 -5
  14. package/lib/components/base/dropdown/__tests__/DropdownActivator.test.js +1 -1
  15. package/lib/components/base/dropdown/__tests__/DropdownOption.test.js +1 -1
  16. package/lib/components/toolbar/Toolbar.vue +29 -13
  17. package/lib/components/toolbar/ToolbarFloating.vue +42 -0
  18. package/lib/components/toolbar/__tests__/Toolbar.test.js +4 -6
  19. package/lib/components/toolbar/controls/LineHeightControl.vue +9 -7
  20. package/lib/components/toolbar/controls/__tests__/LineHeightControl.test.js +2 -4
  21. package/lib/components/toolbar/controls/link/LinkControl.vue +13 -14
  22. package/lib/components/toolbar/controls/link/destination/LinkControlDestination.vue +1 -1
  23. package/lib/components/toolbar/controls/link/destination/LinkControlPageBlock.vue +1 -1
  24. package/lib/composables/__tests__/useEditor.test.js +26 -5
  25. package/lib/composables/index.js +0 -1
  26. package/lib/composables/useEditor.js +7 -2
  27. package/lib/styles/content.css +0 -1
  28. package/package.json +50 -51
  29. package/lib/components/base/composables/__tests__/useModalToggler.test.js +0 -59
  30. package/lib/composables/useToolbar.js +0 -36
  31. /package/config/build/{example.config.js → example.config.mjs} +0 -0
  32. /package/config/build/{lib.config.js → lib.config.mjs} +0 -0
@@ -1,11 +1,16 @@
1
1
  <template>
2
2
  <div ref="wrapperRef">
3
- <Button icon skin="toolbar" :active="isActive" @click="toggler.open">
3
+ <Button icon skin="toolbar" :active="toggler.isOpened" @click="toggler.open">
4
4
  <Icon name="sparkles" size="28px" />
5
5
  <span class="zw-ai-control__caption">AI</span>
6
6
  </Button>
7
7
 
8
- <Modal ref="modalRef" class="zw-ai-component__modal" :toggler="toggler" focus-first-control>
8
+ <Modal
9
+ class="zw-ai-component__modal"
10
+ :toggler="toggler"
11
+ :reference-ref="wrapperRef"
12
+ focus-first-control
13
+ >
9
14
  <p>Modal content</p>
10
15
 
11
16
  <Button skin="primary" @click="onInsert">
@@ -15,57 +20,18 @@
15
20
  </div>
16
21
  </template>
17
22
 
18
- <script>
19
- import { inject, ref, unref, computed } from 'vue';
23
+ <script setup>
24
+ import { inject, ref } from 'vue';
20
25
  import { Button, Icon, useModalToggler, Modal } from '../../lib/components/base';
21
26
  import { InjectionTokens } from '../../lib/injectionTokens';
22
27
 
23
- export default {
24
- name: 'AiComponent',
28
+ const editor = inject(InjectionTokens.EDITOR);
29
+ const wrapperRef = ref(null);
25
30
 
26
- components: {
27
- Modal,
28
- Button,
29
- Icon
30
- },
31
+ const toggler = useModalToggler();
31
32
 
32
- setup() {
33
- const editor = inject(InjectionTokens.EDITOR);
34
- const wrapperRef = ref(null);
35
- const modalRef = ref(null);
36
-
37
- const onBeforeOpened = () => {};
38
-
39
- const toggler = useModalToggler({
40
- options: {
41
- placement: 'bottom-start',
42
- strategy: 'absolute',
43
- offset: [-8, 5]
44
- },
45
- onBeforeOpened: () => onBeforeOpened(),
46
- wrapperRef,
47
- modalRef
48
- });
49
-
50
- const onInsert = () => {
51
- editor.chain()
52
- .focus()
53
- .insertContent('Hello from AI component')
54
- .setDocMeta('ai_generated', true)
55
- .run();
56
- toggler.close();
57
- };
58
-
59
- const isActive = computed(() => unref(toggler.isOpened));
60
-
61
- return {
62
- editor,
63
- wrapperRef,
64
- modalRef,
65
- toggler,
66
- onInsert,
67
- isActive
68
- };
69
- }
70
- };
33
+ function onInsert() {
34
+ editor.chain().focus().insertContent('Hello from AI component').run();
35
+ toggler.close();
36
+ }
71
37
  </script>
package/lib/Wysiwyg.vue CHANGED
@@ -1,11 +1,13 @@
1
1
  <template>
2
2
  <div class="zw-wysiwyg" ref="wysiwygRef">
3
3
  <Toolbar
4
- :toolbar="toolbar"
5
4
  :device="device"
5
+ :visible="isToolbarVisible"
6
6
  :popup-mode="popupMode"
7
+ :reference-ref="wysiwygRef"
8
+ :placement="toolbarPlacement"
9
+ :offsets="toolbarOffsets"
7
10
  :ai-component="aiComponent"
8
- ref="toolbarRef"
9
11
  />
10
12
 
11
13
  <EditorContent :editor="editor" />
@@ -16,7 +18,7 @@
16
18
  import { EditorContent } from '@tiptap/vue-3';
17
19
  import { provide, toRef, ref, computed } from 'vue';
18
20
  import { Toolbar } from './components';
19
- import { useToolbar, useEditor } from './composables';
21
+ import { useEditor } from './composables';
20
22
  import { buildExtensions } from './extensions';
21
23
  import { InjectionTokens } from './injectionTokens';
22
24
  import { ContextWindow, FavoriteColors, Storage } from './services';
@@ -137,27 +139,17 @@ const MAX_FONT_SIZE = 112;
137
139
  ContextWindow.use(props.window);
138
140
 
139
141
  const fonts = props.fonts.map((font) => new Font(font));
140
- const toolbarRef = ref(null);
141
142
  const wysiwygRef = ref(null);
142
- const wrapperRef = computed(() => wysiwygRef.value?.$el || document.body);
143
- const isToolbarActiveRef = computed(() => props.active && !props.readonly);
144
-
145
- const toolbar = useToolbar({
146
- wrapperRef: wysiwygRef,
147
- placementRef: toRef(props, 'toolbarPlacement'),
148
- isActiveRef: isToolbarActiveRef,
149
- offsets: props.toolbarOffsets
150
- });
143
+ const wrapperRef = computed(() => wysiwygRef.value || document.body);
144
+ const isToolbarVisible = computed(() => props.active && !props.readonly);
151
145
 
152
- function onChange(content) {
153
- emit('update:model-value', content);
154
- }
146
+ const onChange = (content) => emit('update:model-value', content);
155
147
 
156
148
  const pageBlocks = toRef(props, 'pageBlocks');
157
149
 
158
150
  const { editor, getContent } = useEditor({
159
151
  content: toRef(props, 'modelValue'),
160
- onChange: (content) => onChange(content),
152
+ onChange,
161
153
  isReadonlyRef: toRef(props, 'readonly'),
162
154
 
163
155
  extensions: buildExtensions({
@@ -1,25 +1,32 @@
1
1
  <template>
2
- <transition name="zw-modal-" :duration="transition">
3
- <div
2
+ <Transition name="zw-modal-" :duration="transitionDuration">
3
+ <ModalFloating
4
4
  class="zw-modal"
5
5
  ref="hostRef"
6
6
  tabindex="-1"
7
- :style="modalStyles"
8
- v-if="isOpened"
7
+ :reference-ref="referenceRef"
9
8
  v-out-click="toggler.close"
9
+ v-if="toggler.isOpened && referenceRef"
10
10
  data-test-selector="modal"
11
11
  >
12
12
  <slot />
13
- </div>
14
- </transition>
13
+ </ModalFloating>
14
+ </Transition>
15
15
  </template>
16
16
 
17
17
  <script setup>
18
- import { computed, nextTick, ref, unref, watch } from 'vue';
18
+ import { nextTick, ref, toRef, watch } from 'vue';
19
19
  import { outClick as vOutClick } from '../../directives';
20
20
  import { useDeselectionLock, useElementRef, useModalToggler } from './composables';
21
+ import ModalFloating from './ModalFloating';
21
22
 
22
23
  const props = defineProps({
24
+ referenceRef: {
25
+ type: Object,
26
+ required: false,
27
+ default: null
28
+ },
29
+
23
30
  toggler: {
24
31
  type: Object,
25
32
  required: false,
@@ -45,28 +52,22 @@ const props = defineProps({
45
52
  }
46
53
  });
47
54
 
48
- const transition = {
55
+ const transitionDuration = {
49
56
  enter: 200,
50
57
  leave: 100
51
58
  };
52
59
 
53
60
  const toggler = props.toggler || useModalToggler();
54
- const isOpened = computed(() => unref(toggler.isOpened));
55
61
  const hostRef = ref(null);
56
62
  const hostEl = useElementRef(hostRef);
57
63
 
58
- const modalStyles = computed(() => ({
59
- '--zw-modal-max-height': `${props.maxHeight}px`,
60
- '--zw-modal-max-width': `${props.maxWidth}px`
61
- }));
62
-
63
64
  useDeselectionLock({
64
- isActiveRef: toggler.isOpened,
65
+ isActiveRef: toRef(toggler, 'isOpened'),
65
66
  hostRef
66
67
  });
67
68
 
68
69
  if (props.focusFirstControl) {
69
- watch(toggler.isOpened, async (_, wasOpened) => {
70
+ watch(toRef(toggler, 'isOpened'), async (_, wasOpened) => {
70
71
  if (wasOpened) return;
71
72
 
72
73
  await nextTick();
@@ -82,8 +83,8 @@ if (props.focusFirstControl) {
82
83
  border-radius: 2px;
83
84
  box-shadow: 0 0 4px rgba(var(--zw-color-black), 0.3);
84
85
  background-color: rgb(var(--zw-color-n15));
85
- max-height: var(--zw-modal-max-height);
86
- max-width: var(--zw-modal-max-width);
86
+ max-height: v-bind("maxHeight + 'px'");
87
+ max-width: v-bind("maxWidth + 'px'");
87
88
  z-index: 1000;
88
89
  position: fixed;
89
90
  will-change: transform;
@@ -0,0 +1,32 @@
1
+ <template>
2
+ <div ref="floatingRef" :style="floatingStyles">
3
+ <slot />
4
+ </div>
5
+ </template>
6
+
7
+ <script setup>
8
+ import { ref, toRef } from 'vue';
9
+ import { useFloating, limitShift, offset, shift, autoUpdate } from '@floating-ui/vue';
10
+
11
+ const props = defineProps({
12
+ referenceRef: {
13
+ type: Object,
14
+ required: true
15
+ }
16
+ });
17
+
18
+ const floatingRef = ref(null);
19
+ const referenceRef = toRef(props, 'referenceRef');
20
+
21
+ const { floatingStyles } = useFloating(referenceRef, floatingRef, {
22
+ placement: 'bottom',
23
+ strategy: 'fixed',
24
+
25
+ middleware: [
26
+ shift({ padding: 16, crossAxis: true, limiter: limitShift() }),
27
+ offset({ mainAxis: 4 })
28
+ ],
29
+
30
+ whileElementsMounted: autoUpdate
31
+ });
32
+ </script>
@@ -22,7 +22,7 @@ function createComponent({ toggler, maxHeight, maxWidth } = {}) {
22
22
  return shallowMount(Modal, {
23
23
  global: {
24
24
  stubs: {
25
- transition: {
25
+ Transition: {
26
26
  render() {
27
27
  return this.$slots.default?.[0]?.();
28
28
  }
@@ -32,6 +32,7 @@ function createComponent({ toggler, maxHeight, maxWidth } = {}) {
32
32
  },
33
33
  props: {
34
34
  toggler: toggler ?? createToggler(true),
35
+ referenceRef: document.createElement('div'),
35
36
  maxHeight: maxHeight ?? 1000,
36
37
  maxWidth: maxWidth ?? 1000
37
38
  }
@@ -63,21 +64,3 @@ describe('open/close', () => {
63
64
  expect(wrapper).toVueContainElement(SELECTORS.MODAL);
64
65
  });
65
66
  });
66
-
67
- describe('rendering', () => {
68
- test('should render max width', () => {
69
- const wrapper = createComponent({ maxWidth: 256 });
70
- const modalWrapper = wrapper.find(SELECTORS.MODAL);
71
- const maxWidth = modalWrapper.element.style.getPropertyValue('--zw-modal-max-width');
72
-
73
- expect(maxWidth).toBe('256px');
74
- });
75
-
76
- test('should render max height', () => {
77
- const wrapper = createComponent({ maxHeight: 256 });
78
- const modalWrapper = wrapper.find(SELECTORS.MODAL);
79
- const maxHeight = modalWrapper.element.style.getPropertyValue('--zw-modal-max-height');
80
-
81
- expect(maxHeight).toBe('256px');
82
- });
83
- });
@@ -1,35 +1,9 @@
1
- import { inject, nextTick, ref } from 'vue';
2
- import { autoUpdate, computePosition, limitShift, offset, shift } from '@floating-ui/dom';
1
+ import { inject, reactive, ref } from 'vue';
3
2
  import { InjectionTokens } from '../../../injectionTokens';
4
- import { useElementRef } from './useElementRef';
5
3
 
6
- export function useModalToggler({ onBeforeOpened, onClosed, wrapperRef, modalRef } = {}) {
4
+ export function useModalToggler({ onBeforeOpened, onClosed } = {}) {
7
5
  const editor = inject(InjectionTokens.EDITOR);
8
6
  const isOpened = ref(false);
9
- let floatingInstance;
10
-
11
- function initModal() {
12
- const wrapperEl = useElementRef(wrapperRef);
13
- const modalEl = useElementRef(modalRef);
14
-
15
- floatingInstance = autoUpdate(wrapperEl.value, modalEl.value, async () => {
16
- const positioning = await computePosition(wrapperEl.value, modalEl.value, {
17
- placement: 'bottom',
18
- strategy: 'fixed',
19
- middleware: [
20
- shift({ padding: 16, crossAxis: true, limiter: limitShift() }),
21
- offset({ mainAxis: 4 })
22
- ]
23
- });
24
-
25
- const { x, y } = positioning;
26
-
27
- Object.assign(modalEl.value, {
28
- left: `${x}px`,
29
- top: `${y}px`
30
- });
31
- });
32
- }
33
7
 
34
8
  async function open() {
35
9
  if (isOpened.value) return;
@@ -37,20 +11,15 @@ export function useModalToggler({ onBeforeOpened, onClosed, wrapperRef, modalRef
37
11
  onBeforeOpened?.();
38
12
  editor.commands.storeSelection();
39
13
  isOpened.value = true;
40
-
41
- await nextTick();
42
-
43
- initModal();
44
14
  }
45
15
 
46
16
  function close() {
47
17
  isOpened.value = false;
48
- floatingInstance?.();
49
18
  editor.commands.restoreSelection();
50
19
  onClosed?.();
51
20
  }
52
21
 
53
22
  const toggle = (toOpen) => toOpen ? open() : close();
54
23
 
55
- return { isOpened, open, close, toggle };
24
+ return reactive({ isOpened, open, close, toggle });
56
25
  }
@@ -6,7 +6,12 @@
6
6
  </template>
7
7
  </DropdownActivator>
8
8
 
9
- <Modal max-height="300" :max-width="maxWidth" :toggler="toggler" ref="modalRef">
9
+ <Modal
10
+ max-height="300"
11
+ :max-width="maxWidth"
12
+ :toggler="toggler"
13
+ :reference-ref="dropdownRef"
14
+ >
10
15
  <DropdownMenu :options="options">
11
16
  <template #option="attrs">
12
17
  <slot name="option" v-bind="attrs" />
@@ -58,7 +63,6 @@ const props = defineProps({
58
63
  const emit = defineEmits(['change']);
59
64
 
60
65
  const dropdownRef = ref(null);
61
- const modalRef = ref(null);
62
66
 
63
67
  const activeOptionManager = useActiveOptionManager({
64
68
  optionsRef: toRef(props, 'options'),
@@ -67,10 +71,7 @@ const activeOptionManager = useActiveOptionManager({
67
71
  onChange: (value) => emit('change', value.id)
68
72
  });
69
73
 
70
- const toggler = useModalToggler({
71
- wrapperRef: dropdownRef,
72
- modalRef
73
- });
74
+ const toggler = useModalToggler();
74
75
 
75
76
  provide(InjectionTokens.ACTIVE_MANAGER, activeOptionManager);
76
77
  provide(InjectionTokens.TOGGLER, toggler);
@@ -1,10 +1,10 @@
1
1
  <template>
2
- <slot :open="dropdownToggler.open" :isOpened="isOpened">
2
+ <slot :open="dropdownToggler.open" :isOpened="dropdownToggler.isOpened">
3
3
  <Button
4
4
  skin="toolbar"
5
5
  class="zw-dropdown__activator"
6
6
  :class="dropdownClasses"
7
- :active="isOpened"
7
+ :active="dropdownToggler.isOpened"
8
8
  @click="dropdownToggler.open"
9
9
  >
10
10
  <span class="zw-dropdown__activator-title zw-text--truncate">
@@ -29,7 +29,7 @@
29
29
  </template>
30
30
 
31
31
  <script setup>
32
- import { computed, inject, toRef, unref } from 'vue';
32
+ import { computed, inject, toRef } from 'vue';
33
33
  import { tooltip as vTooltip } from '../../../directives';
34
34
  import Button from '../Button';
35
35
  import Icon from '../Icon';
@@ -52,13 +52,12 @@ const props = defineProps({
52
52
 
53
53
  const activeOptionManager = inject(InjectionTokens.ACTIVE_MANAGER);
54
54
  const dropdownToggler = inject(InjectionTokens.TOGGLER);
55
- const isOpened = computed(() => unref(dropdownToggler.isOpened));
56
55
  const color = toRef(props, 'color');
57
56
 
58
57
  const activeOptionTitle = useDropdownEntityTitle(activeOptionManager.activeOption);
59
58
 
60
59
  const dropdownClasses = computed(() => ({
61
- 'zw-dropdown__activator--active': dropdownToggler.isOpened.value,
60
+ 'zw-dropdown__activator--active': dropdownToggler.isOpened,
62
61
  'zw-dropdown__activator--gray': color.value === 'gray'
63
62
  }));
64
63
  </script>
@@ -9,7 +9,7 @@ const SELECTORS = {
9
9
  };
10
10
 
11
11
  const createToggler = ({ isOpened } = {}) => ({
12
- isOpened: ref(isOpened ?? false),
12
+ isOpened: isOpened ?? false,
13
13
  open: jest.fn()
14
14
  });
15
15
 
@@ -11,7 +11,7 @@ const createActiveManager = ({ activeOption } = {}) => ({
11
11
  });
12
12
 
13
13
  const createToggler = () => ({
14
- isOpened: ref(true),
14
+ isOpened: true,
15
15
  close: jest.fn()
16
16
  });
17
17
 
@@ -1,17 +1,24 @@
1
1
  <template>
2
2
  <keep-alive>
3
3
  <transition name="zw-toolbar-" duration="150">
4
- <div class="zw-toolbar" ref="toolbarRef" v-if="isVisible">
4
+ <ToolbarFloating
5
+ class="zw-toolbar"
6
+ :reference-ref="referenceRef"
7
+ :placement="placement"
8
+ :offsets="offsets"
9
+ v-if="visible && referenceRef"
10
+ >
5
11
  <component :is="layoutComponent" :ai-component="aiComponent" />
6
- </div>
12
+ </ToolbarFloating>
7
13
  </transition>
8
14
  </keep-alive>
9
15
  </template>
10
16
 
11
17
  <script setup>
12
- import { computed, ref, watch } from 'vue';
18
+ import { computed } from 'vue';
13
19
  import { Devices } from '../../enums';
14
20
  import { ToolbarDesktop, ToolbarMobile, ToolbarPopup } from './layouts';
21
+ import ToolbarFloating from './ToolbarFloating';
15
22
 
16
23
  const props = defineProps({
17
24
  device: {
@@ -19,8 +26,8 @@ const props = defineProps({
19
26
  required: true
20
27
  },
21
28
 
22
- toolbar: {
23
- type: Object,
29
+ visible: {
30
+ type: Boolean,
24
31
  required: true
25
32
  },
26
33
 
@@ -29,6 +36,22 @@ const props = defineProps({
29
36
  required: true
30
37
  },
31
38
 
39
+ referenceRef: {
40
+ type: Object,
41
+ required: false,
42
+ default: null
43
+ },
44
+
45
+ placement: {
46
+ type: String,
47
+ required: true
48
+ },
49
+
50
+ offsets: {
51
+ type: Array,
52
+ required: true
53
+ },
54
+
32
55
  aiComponent: {
33
56
  type: Object,
34
57
  required: false,
@@ -41,13 +64,6 @@ const layoutComponent = computed(() => {
41
64
 
42
65
  return props.device === Devices.MOBILE ? ToolbarMobile : ToolbarDesktop;
43
66
  });
44
-
45
- const isVisible = computed(() => props.toolbar.isActiveRef.value);
46
- const toolbarRef = ref(null);
47
-
48
- watch(toolbarRef, (toolbarEl) => {
49
- toolbarEl && props.toolbar.mount(toolbarEl);
50
- });
51
67
  </script>
52
68
 
53
69
  <style scoped>
@@ -61,7 +77,7 @@ watch(toolbarRef, (toolbarEl) => {
61
77
  z-index: 999999;
62
78
  text-align: left;
63
79
  position: fixed;
64
- --zw-toolbar-offset-y: calc(v-bind("toolbar.offsets[1]") * 1px)
80
+ --zw-toolbar-offset-y: calc(v-bind("offsets[1]") * 1px)
65
81
  }
66
82
 
67
83
  .zw-toolbar::before,
@@ -0,0 +1,42 @@
1
+ <template>
2
+ <div ref="floatingRef" :style="floatingStyles">
3
+ <slot />
4
+ </div>
5
+ </template>
6
+
7
+ <script setup>
8
+ import { computed, ref, toRef } from 'vue';
9
+ import { useFloating, limitShift, offset, shift, autoUpdate } from '@floating-ui/vue';
10
+
11
+ const props = defineProps({
12
+ referenceRef: {
13
+ type: Object,
14
+ required: true
15
+ },
16
+
17
+ placement: {
18
+ type: String,
19
+ required: true
20
+ },
21
+
22
+ offsets: {
23
+ type: Array,
24
+ required: true
25
+ }
26
+ });
27
+
28
+ const floatingRef = ref(null);
29
+ const referenceRef = toRef(props, 'referenceRef');
30
+
31
+ const middlewares = computed(() => [
32
+ shift({ padding: 16, crossAxis: true, limiter: limitShift() }),
33
+ offset({ crossAxis: props.offsets[0], mainAxis: props.offsets[1] })
34
+ ]);
35
+
36
+ const { floatingStyles } = useFloating(referenceRef, floatingRef, {
37
+ placement: toRef(props, 'placement'),
38
+ strategy: 'fixed',
39
+ middleware: middlewares,
40
+ whileElementsMounted: autoUpdate
41
+ });
42
+ </script>
@@ -1,5 +1,4 @@
1
1
  import { shallowMount } from '@vue/test-utils';
2
- import { ref } from 'vue';
3
2
  import { Devices } from '../../../enums';
4
3
  import Toolbar from '../Toolbar';
5
4
  import { ToolbarMobile, ToolbarDesktop, ToolbarPopup } from '../layouts';
@@ -7,12 +6,11 @@ import { ToolbarMobile, ToolbarDesktop, ToolbarPopup } from '../layouts';
7
6
  function createComponent({ device, popupMode }) {
8
7
  return shallowMount(Toolbar, {
9
8
  props: {
10
- toolbar: {
11
- mount: jest.fn(),
12
- isActiveRef: ref({ value: true }),
13
- offsets: [0, 8]
14
- },
9
+ visible: true,
10
+ offsets: [0, 8],
11
+ referenceRef: document.createElement('div'),
15
12
  device: device ?? Devices.DESKTOP,
13
+ placement: 'bottom',
16
14
  popupMode: popupMode ?? false
17
15
  }
18
16
  });
@@ -4,7 +4,7 @@
4
4
  class="zw-position--relative"
5
5
  icon
6
6
  skin="toolbar"
7
- :active="isOpened"
7
+ :active="toggler.isOpened"
8
8
  @click="toggler.open"
9
9
  v-tooltip="'Line Height'"
10
10
  >
@@ -18,7 +18,11 @@
18
18
  />
19
19
  </Button>
20
20
 
21
- <Modal class="zw-line-height-control__modal" ref="modalRef" :toggler="toggler">
21
+ <Modal
22
+ class="zw-line-height-control__modal"
23
+ :toggler="toggler"
24
+ :reference-ref="wrapperRef"
25
+ >
22
26
  <FieldLabel class="zw-margin-bottom--xs" field-id="wswg-line-height">
23
27
  Line Height
24
28
  </FieldLabel>
@@ -48,22 +52,20 @@
48
52
  </template>
49
53
 
50
54
  <script setup>
51
- import { computed, inject, ref, unref } from 'vue';
55
+ import { inject, ref } from 'vue';
52
56
  import { Button, Icon, Modal, Range, NumberField, FieldLabel, useModalToggler } from '../../base';
53
57
  import { InjectionTokens } from '../../../injectionTokens';
54
58
  import { tooltip as vTooltip } from '../../../directives';
55
59
  import { TextSettings } from '../../../enums';
56
60
 
57
- const wrapperRef = ref(null);
58
- const modalRef = ref(null);
59
61
  const editor = inject(InjectionTokens.EDITOR);
60
- const toggler = useModalToggler({ wrapperRef, modalRef });
62
+ const wrapperRef = ref(null);
63
+ const toggler = useModalToggler();
61
64
 
62
65
  const currentValue = editor.commands.getLineHeight();
63
66
  const isCustomized = editor.commands.isSettingCustomized(TextSettings.LINE_HEIGHT);
64
67
 
65
68
  const apply = (value) => editor.commands.applyLineHeight(String(value));
66
- const isOpened = computed(() => unref(toggler.isOpened));
67
69
  </script>
68
70
 
69
71
  <style scoped>
@@ -4,8 +4,6 @@ import { InjectionTokens } from '../../../../injectionTokens';
4
4
  import { Button, Modal, NumberField, Range } from '../../../base';
5
5
  import LineHeightControl from '../LineHeightControl';
6
6
 
7
- jest.mock('@floating-ui/dom');
8
-
9
7
  const SELECTORS = {
10
8
  INDICATOR: '[data-test-selector="customizedIndicator"]'
11
9
  };
@@ -112,7 +110,7 @@ describe('rendering', () => {
112
110
  const modalWrapper = wrapper.findComponent(Modal);
113
111
 
114
112
  expect(buttonWrapper.props('active')).toBe(false);
115
- expect(modalWrapper.props('toggler').isOpened.value).toBe(false);
113
+ expect(modalWrapper.props('toggler').isOpened).toBe(false);
116
114
  });
117
115
 
118
116
  test('should open modal', async () => {
@@ -124,7 +122,7 @@ describe('rendering', () => {
124
122
  await nextTick();
125
123
 
126
124
  expect(buttonWrapper.props('active')).toBe(true);
127
- expect(modalWrapper.props('toggler').isOpened.value).toBe(true);
125
+ expect(modalWrapper.props('toggler').isOpened).toBe(true);
128
126
  });
129
127
 
130
128
  test('should render indicator of customized styles', () => {