@zipify/wysiwyg 4.12.0-beta.3 → 4.13.0-beta.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/cli.js +25 -25
- package/dist/node.js +19 -19
- package/dist/types/components/toolbar/controls/LetterSpacingControl.vue.d.ts +2 -0
- package/dist/types/enums/TextSetting.d.ts +1 -0
- package/dist/types/extensions/LetterSpacing.d.ts +2 -0
- package/dist/types/utils/convertEmToPx.d.ts +1 -0
- package/dist/types/utils/convertLetterSpacing.d.ts +1 -0
- package/dist/types/utils/index.d.ts +1 -0
- package/dist/wysiwyg.css +46 -28
- package/dist/wysiwyg.mjs +312 -148
- package/example/presets.js +227 -203
- package/lib/assets/icons/letter-spacing.svg +3 -0
- package/lib/components/base/NumberField.vue +2 -2
- package/lib/components/toolbar/controls/LetterSpacingControl.vue +96 -0
- package/lib/components/toolbar/controls/index.js +1 -0
- package/lib/components/toolbar/layouts/ToolbarDesktop.vue +2 -0
- package/lib/components/toolbar/layouts/ToolbarMobile.vue +2 -0
- package/lib/enums/TextSetting.ts +2 -0
- package/lib/extensions/LetterSpacing.js +79 -0
- package/lib/extensions/__tests__/LetterSpacing.test.js +129 -0
- package/lib/extensions/__tests__/__snapshots__/LetterSpacing.test.js.snap +151 -0
- package/lib/extensions/core/NodeProcessor.js +1 -1
- package/lib/extensions/index.js +2 -0
- package/lib/styles/content.css +3 -0
- package/lib/utils/convertEmToPx.ts +8 -0
- package/lib/utils/convertFontSize.js +2 -7
- package/lib/utils/convertLetterSpacing.ts +5 -0
- package/lib/utils/index.ts +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Mark } from '@tiptap/vue-3';
|
|
2
|
+
import { computed, unref } from 'vue';
|
|
3
|
+
import { createCommand, renderMark, convertLetterSpacing } from '../utils';
|
|
4
|
+
import { MarkGroup, TextSetting } from '../enums';
|
|
5
|
+
|
|
6
|
+
export const LetterSpacing = Mark.create({
|
|
7
|
+
name: TextSetting.LETTER_SPACING,
|
|
8
|
+
group: MarkGroup.SETTINGS,
|
|
9
|
+
|
|
10
|
+
addAttributes: () => ({
|
|
11
|
+
mobile: { default: null },
|
|
12
|
+
tablet: { default: null },
|
|
13
|
+
desktop: { default: null }
|
|
14
|
+
}),
|
|
15
|
+
|
|
16
|
+
addCommands() {
|
|
17
|
+
return {
|
|
18
|
+
getLetterSpacing: createCommand(({ commands }) => {
|
|
19
|
+
return commands.getDeviceSettingMark(this.name, commands.getDefaultLetterSpacing());
|
|
20
|
+
}),
|
|
21
|
+
|
|
22
|
+
getDefaultLetterSpacing: createCommand(({ commands }) => {
|
|
23
|
+
const device = commands.getDevice();
|
|
24
|
+
const preset = commands.getPreset();
|
|
25
|
+
|
|
26
|
+
return computed(() => unref(preset)[unref(device)].letter_spacing ?? null);
|
|
27
|
+
}),
|
|
28
|
+
|
|
29
|
+
applyLetterSpacing: createCommand(({ commands }, value) => {
|
|
30
|
+
const targetDevices = commands.getTargetDevices();
|
|
31
|
+
const attrs = Object.fromEntries(targetDevices.map((device) => [device, value]));
|
|
32
|
+
|
|
33
|
+
commands.applyMark(this.name, attrs);
|
|
34
|
+
})
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
parseHTML() {
|
|
39
|
+
const parse = (value) => {
|
|
40
|
+
if (!value) return null;
|
|
41
|
+
|
|
42
|
+
const wrapperEl = unref(this.options.wrapperRef);
|
|
43
|
+
const converted = convertLetterSpacing(value, wrapperEl);
|
|
44
|
+
|
|
45
|
+
return `${converted}px`;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
return [
|
|
49
|
+
{
|
|
50
|
+
tag: '[style*="--zw-letter-spacing"]',
|
|
51
|
+
|
|
52
|
+
getAttrs: ({ style }) => ({
|
|
53
|
+
mobile: parse(style.getPropertyValue('--zw-letter-spacing-mobile')),
|
|
54
|
+
tablet: parse(style.getPropertyValue('--zw-letter-spacing-tablet')),
|
|
55
|
+
desktop: parse(style.getPropertyValue('--zw-letter-spacing-desktop'))
|
|
56
|
+
})
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
style: 'letter-spacing',
|
|
60
|
+
|
|
61
|
+
getAttrs: (input) => {
|
|
62
|
+
const value = parse(input);
|
|
63
|
+
|
|
64
|
+
return { desktop: value, tablet: value, mobile: value };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
];
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
renderHTML({ HTMLAttributes: attrs }) {
|
|
71
|
+
const addUnits = (value) => value ? `${value}px` : null;
|
|
72
|
+
|
|
73
|
+
return renderMark({
|
|
74
|
+
letter_spacing_mobile: addUnits(attrs.mobile),
|
|
75
|
+
letter_spacing_tablet: addUnits(attrs.tablet),
|
|
76
|
+
letter_spacing_desktop: addUnits(attrs.desktop)
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
});
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { Editor, Extension } from '@tiptap/vue-3';
|
|
2
|
+
import { ref } from 'vue';
|
|
3
|
+
import { buildTestExtensions } from '../../__tests__/utils';
|
|
4
|
+
import { createCommand } from '../../utils';
|
|
5
|
+
import { DeviceManager } from '../DeviceManager';
|
|
6
|
+
import { LetterSpacing } from '../LetterSpacing';
|
|
7
|
+
import { ContentNormalizer, NodeFactory } from '../../services';
|
|
8
|
+
import { Device, TextSetting } from '../../enums';
|
|
9
|
+
|
|
10
|
+
const MockStylePreset = Extension.create({
|
|
11
|
+
name: TextSetting.STYLE_PRESET,
|
|
12
|
+
|
|
13
|
+
addCommands() {
|
|
14
|
+
return {
|
|
15
|
+
getPreset: createCommand(() => ref({
|
|
16
|
+
mobile: { letter_spacing: null },
|
|
17
|
+
tablet: { letter_spacing: null },
|
|
18
|
+
desktop: { letter_spacing: null }
|
|
19
|
+
}))
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
function createEditor({ content, wrapperEl, device = Device.DESKTOP } = {}) {
|
|
25
|
+
return new Editor({
|
|
26
|
+
content: ContentNormalizer.normalize(content),
|
|
27
|
+
extensions: buildTestExtensions({
|
|
28
|
+
include: [
|
|
29
|
+
MockStylePreset,
|
|
30
|
+
DeviceManager.configure({
|
|
31
|
+
device: ref(device)
|
|
32
|
+
}),
|
|
33
|
+
LetterSpacing.configure({ wrapperRef: ref(wrapperEl ?? document.createElement('div')) })
|
|
34
|
+
]
|
|
35
|
+
})
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const createContent = (attrs) => NodeFactory.doc([
|
|
40
|
+
NodeFactory.paragraph(attrs, [
|
|
41
|
+
NodeFactory.text('hello world')
|
|
42
|
+
])
|
|
43
|
+
]);
|
|
44
|
+
|
|
45
|
+
describe('LetterSpacing extension', () => {
|
|
46
|
+
describe('get value', () => {
|
|
47
|
+
test('should get from selection', () => {
|
|
48
|
+
const editor = createEditor({
|
|
49
|
+
content: NodeFactory.doc([
|
|
50
|
+
NodeFactory.paragraph({}, [
|
|
51
|
+
NodeFactory.text('hello world', [
|
|
52
|
+
NodeFactory.mark(TextSetting.LETTER_SPACING, {
|
|
53
|
+
mobile: '',
|
|
54
|
+
tablet: '',
|
|
55
|
+
desktop: '-0.7px'
|
|
56
|
+
})
|
|
57
|
+
])
|
|
58
|
+
])
|
|
59
|
+
])
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
editor.commands.selectAll();
|
|
63
|
+
|
|
64
|
+
expect(editor.commands.getLetterSpacing().value).toBe('-0.7px');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('should get default value from preset when null', () => {
|
|
68
|
+
const editor = createEditor({
|
|
69
|
+
content: createContent({ letter_spacing: null })
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
expect(editor.commands.getDefaultLetterSpacing().value).toBe(null);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe('apply value', () => {
|
|
77
|
+
test('should change value', () => {
|
|
78
|
+
const editor = createEditor({
|
|
79
|
+
content: createContent({ letter_spacing: null })
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
editor.commands.selectAll();
|
|
83
|
+
editor.commands.applyLetterSpacing('-1');
|
|
84
|
+
|
|
85
|
+
expect(editor.getJSON()).toMatchSnapshot();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('should apply value 0', () => {
|
|
89
|
+
const editor = createEditor({
|
|
90
|
+
content: createContent({ letter_spacing: 2 })
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
editor.commands.selectAll();
|
|
94
|
+
editor.commands.applyLetterSpacing(0);
|
|
95
|
+
|
|
96
|
+
expect(editor.getJSON()).toMatchSnapshot();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('should change value on all devices', () => {
|
|
100
|
+
const editor = createEditor({
|
|
101
|
+
content: createContent({ letter_spacing: null }),
|
|
102
|
+
device: Device.ALL
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
editor.commands.selectAll();
|
|
106
|
+
editor.commands.applyLetterSpacing('-1');
|
|
107
|
+
|
|
108
|
+
expect(editor.getJSON()).toMatchSnapshot();
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
describe('parsing html', () => {
|
|
113
|
+
test('should parse inline letter-spacing px to value with px preserved', () => {
|
|
114
|
+
const editor = createEditor({
|
|
115
|
+
content: '<p style="letter-spacing: -0.7px">test</p>'
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
expect(editor.getJSON()).toMatchSnapshot();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('should parse custom var style', () => {
|
|
122
|
+
const editor = createEditor({
|
|
123
|
+
content: '<p style="--zw-letter-spacing-desktop: -0.7px;--zw-letter-spacing-tablet: 0px;--zw-letter-spacing-mobile: 3.6px">test</p>'
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
expect(editor.getJSON()).toMatchSnapshot();
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
});
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`LetterSpacing extension apply value should apply value 0 1`] = `
|
|
4
|
+
Object {
|
|
5
|
+
"attrs": Object {
|
|
6
|
+
"meta": Object {},
|
|
7
|
+
},
|
|
8
|
+
"content": Array [
|
|
9
|
+
Object {
|
|
10
|
+
"content": Array [
|
|
11
|
+
Object {
|
|
12
|
+
"text": "hello world",
|
|
13
|
+
"type": "text",
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
"marks": Array [
|
|
17
|
+
Object {
|
|
18
|
+
"attrs": Object {
|
|
19
|
+
"desktop": 0,
|
|
20
|
+
"mobile": null,
|
|
21
|
+
"tablet": null,
|
|
22
|
+
},
|
|
23
|
+
"type": "letter_spacing",
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
"type": "paragraph",
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
"type": "doc",
|
|
30
|
+
}
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
exports[`LetterSpacing extension apply value should change value 1`] = `
|
|
34
|
+
Object {
|
|
35
|
+
"attrs": Object {
|
|
36
|
+
"meta": Object {},
|
|
37
|
+
},
|
|
38
|
+
"content": Array [
|
|
39
|
+
Object {
|
|
40
|
+
"content": Array [
|
|
41
|
+
Object {
|
|
42
|
+
"text": "hello world",
|
|
43
|
+
"type": "text",
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
"marks": Array [
|
|
47
|
+
Object {
|
|
48
|
+
"attrs": Object {
|
|
49
|
+
"desktop": "-1",
|
|
50
|
+
"mobile": null,
|
|
51
|
+
"tablet": null,
|
|
52
|
+
},
|
|
53
|
+
"type": "letter_spacing",
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
"type": "paragraph",
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
"type": "doc",
|
|
60
|
+
}
|
|
61
|
+
`;
|
|
62
|
+
|
|
63
|
+
exports[`LetterSpacing extension apply value should change value on all devices 1`] = `
|
|
64
|
+
Object {
|
|
65
|
+
"attrs": Object {
|
|
66
|
+
"meta": Object {},
|
|
67
|
+
},
|
|
68
|
+
"content": Array [
|
|
69
|
+
Object {
|
|
70
|
+
"content": Array [
|
|
71
|
+
Object {
|
|
72
|
+
"text": "hello world",
|
|
73
|
+
"type": "text",
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
"marks": Array [
|
|
77
|
+
Object {
|
|
78
|
+
"attrs": Object {
|
|
79
|
+
"desktop": "-1",
|
|
80
|
+
"mobile": "-1",
|
|
81
|
+
"tablet": "-1",
|
|
82
|
+
},
|
|
83
|
+
"type": "letter_spacing",
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
"type": "paragraph",
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
"type": "doc",
|
|
90
|
+
}
|
|
91
|
+
`;
|
|
92
|
+
|
|
93
|
+
exports[`LetterSpacing extension parsing html should parse custom var style 1`] = `
|
|
94
|
+
Object {
|
|
95
|
+
"attrs": Object {
|
|
96
|
+
"meta": Object {},
|
|
97
|
+
},
|
|
98
|
+
"content": Array [
|
|
99
|
+
Object {
|
|
100
|
+
"content": Array [
|
|
101
|
+
Object {
|
|
102
|
+
"text": "test",
|
|
103
|
+
"type": "text",
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
"marks": Array [
|
|
107
|
+
Object {
|
|
108
|
+
"attrs": Object {
|
|
109
|
+
"desktop": "-0.7px",
|
|
110
|
+
"mobile": "3.6px",
|
|
111
|
+
"tablet": "0px",
|
|
112
|
+
},
|
|
113
|
+
"type": "letter_spacing",
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
"type": "paragraph",
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
"type": "doc",
|
|
120
|
+
}
|
|
121
|
+
`;
|
|
122
|
+
|
|
123
|
+
exports[`LetterSpacing extension parsing html should parse inline letter-spacing px to value with px preserved 1`] = `
|
|
124
|
+
Object {
|
|
125
|
+
"attrs": Object {
|
|
126
|
+
"meta": Object {},
|
|
127
|
+
},
|
|
128
|
+
"content": Array [
|
|
129
|
+
Object {
|
|
130
|
+
"content": Array [
|
|
131
|
+
Object {
|
|
132
|
+
"text": "test",
|
|
133
|
+
"type": "text",
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
"marks": Array [
|
|
137
|
+
Object {
|
|
138
|
+
"attrs": Object {
|
|
139
|
+
"desktop": "-0.7px",
|
|
140
|
+
"mobile": "-0.7px",
|
|
141
|
+
"tablet": "-0.7px",
|
|
142
|
+
},
|
|
143
|
+
"type": "letter_spacing",
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
"type": "paragraph",
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
"type": "doc",
|
|
150
|
+
}
|
|
151
|
+
`;
|
|
@@ -141,7 +141,7 @@ export const NodeProcessor = Extension.create({
|
|
|
141
141
|
for (const attrs of unref(selectionRef)) {
|
|
142
142
|
const value = attrs[unref(deviceRef)];
|
|
143
143
|
|
|
144
|
-
if (value) return value;
|
|
144
|
+
if (value || value === 0) return value;
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
return unref(defaultRef);
|
package/lib/extensions/index.js
CHANGED
|
@@ -13,6 +13,7 @@ import { TextDecoration } from './TextDecoration';
|
|
|
13
13
|
import { CaseStyle } from './CaseStyle';
|
|
14
14
|
import { Alignment } from './Alignment';
|
|
15
15
|
import { LineHeight } from './LineHeight';
|
|
16
|
+
import { LetterSpacing } from './LetterSpacing';
|
|
16
17
|
import { List } from './list';
|
|
17
18
|
import { Link } from './Link';
|
|
18
19
|
import { Superscript } from './Superscript';
|
|
@@ -66,6 +67,7 @@ export function buildExtensions(options) {
|
|
|
66
67
|
LineHeight.configure({
|
|
67
68
|
wrapperRef: options.wrapperRef
|
|
68
69
|
}),
|
|
70
|
+
LetterSpacing,
|
|
69
71
|
Link.configure({
|
|
70
72
|
preset: toRef(presetsMap, 'link'),
|
|
71
73
|
basePresetClass: options.basePresetClass,
|
package/lib/styles/content.css
CHANGED
|
@@ -36,6 +36,7 @@ h4.zw-style.zw-style.zw-style {
|
|
|
36
36
|
font-size: var(--zw-font-size-desktop, var(--zw-preset-font-size-desktop));
|
|
37
37
|
text-align: var(--zw-alignment-desktop, var(--zw-preset-alignment-desktop));
|
|
38
38
|
line-height: var(--zw-line-height-desktop, var(--zw-preset-line-height-desktop));
|
|
39
|
+
letter-spacing: var(--zw-letter-spacing-desktop, var(--zw-preset-letter-spacing-desktop));
|
|
39
40
|
}
|
|
40
41
|
}
|
|
41
42
|
|
|
@@ -45,6 +46,7 @@ h4.zw-style.zw-style.zw-style {
|
|
|
45
46
|
font-size: var(--zw-font-size-tablet, var(--zw-preset-font-size-tablet));
|
|
46
47
|
text-align: var(--zw-alignment-tablet, var(--zw-preset-alignment-tablet));
|
|
47
48
|
line-height: var(--zw-line-height-tablet, var(--zw-preset-line-height-tablet));
|
|
49
|
+
letter-spacing: var(--zw-letter-spacing-tablet, var(--zw-preset-letter-spacing-tablet));
|
|
48
50
|
}
|
|
49
51
|
}
|
|
50
52
|
|
|
@@ -54,6 +56,7 @@ h4.zw-style.zw-style.zw-style {
|
|
|
54
56
|
font-size: var(--zw-font-size-mobile, var(--zw-preset-font-size-mobile));
|
|
55
57
|
text-align: var(--zw-alignment-mobile, var(--zw-preset-alignment-mobile));
|
|
56
58
|
line-height: var(--zw-line-height-mobile, var(--zw-preset-line-height-mobile));
|
|
59
|
+
letter-spacing: var(--zw-letter-spacing-mobile, var(--zw-preset-letter-spacing-mobile));
|
|
57
60
|
}
|
|
58
61
|
}
|
|
59
62
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ContextWindow } from '../services';
|
|
2
|
+
|
|
3
|
+
export function convertEmToPx(value: string, wrapperEl: HTMLElement): number {
|
|
4
|
+
const containerValue = ContextWindow.getComputedStyle(wrapperEl).fontSize;
|
|
5
|
+
const size = parseFloat(value) * parseFloat(containerValue);
|
|
6
|
+
|
|
7
|
+
return Math.round(size * 10) / 10;
|
|
8
|
+
}
|
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { convertEmToPx } from './convertEmToPx';
|
|
2
2
|
|
|
3
3
|
export function convertFontSize(value, wrapperEl) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const containerValue = ContextWindow.getComputedStyle(wrapperEl).fontSize;
|
|
7
|
-
const size = parseFloat(value) * parseFloat(containerValue);
|
|
8
|
-
|
|
9
|
-
return Math.round(size);
|
|
4
|
+
return String(value).includes('em') ? Math.round(convertEmToPx(value, wrapperEl)) : parseInt(value);
|
|
10
5
|
}
|
package/lib/utils/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ export { createKeyboardShortcut } from './createKeyboardShortcut';
|
|
|
5
5
|
export { convertColor } from './convertColor';
|
|
6
6
|
export { convertLineHeight } from './convertLineHeight';
|
|
7
7
|
export { convertFontSize } from './convertFontSize';
|
|
8
|
+
export { convertLetterSpacing } from './convertLetterSpacing';
|
|
8
9
|
export { convertAlignment } from './convertAlignment';
|
|
9
10
|
export { importIcon } from './importIcon';
|
|
10
11
|
export { isWysiwygContent } from './isWysiwygContent';
|