@tailor-cms/ce-quill-html-edit 0.0.3 → 0.0.5
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/components/Edit.vue.d.ts +19 -0
- package/dist/components/Edit.vue.js +106 -0
- package/dist/components/QuillEditor.vue.d.ts +82 -0
- package/dist/components/QuillEditor.vue.js +117 -0
- package/dist/components/TopToolbar.vue.d.ts +2 -0
- package/dist/components/TopToolbar.vue.js +214 -0
- package/dist/components/theme/index.d.ts +33 -0
- package/dist/components/theme/index.js +157 -0
- package/dist/components/theme/modules/image-embed.d.ts +5 -0
- package/dist/components/theme/modules/image-embed.js +18 -0
- package/dist/components/theme/toolbar-icons.d.ts +3 -0
- package/dist/components/theme/toolbar-icons.js +69 -0
- package/dist/components/theme/ui/color-picker.d.ts +16 -0
- package/dist/components/theme/ui/color-picker.js +54 -0
- package/dist/components/theme/ui/image-embed-tooltip.d.ts +15 -0
- package/dist/components/theme/ui/image-embed-tooltip.js +84 -0
- package/dist/components/theme/ui/tooltip.d.ts +9 -0
- package/dist/components/theme/ui/tooltip.js +31 -0
- package/dist/index.cjs +12086 -41110
- package/dist/index.css +12 -506
- package/dist/index.d.ts +6 -0
- package/dist/index.js +12088 -41112
- package/package.json +6 -4
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import IconPicker from 'quill/ui/icon-picker';
|
|
2
|
+
import Picker from 'quill/ui/picker';
|
|
3
|
+
import Quill from 'quill';
|
|
4
|
+
import SnowTheme from 'quill/themes/snow';
|
|
5
|
+
import tippy from 'tippy.js';
|
|
6
|
+
import mdiIcons, { getMdiIcon } from './toolbar-icons.ts';
|
|
7
|
+
import ColorPicker from './ui/color-picker.ts';
|
|
8
|
+
import ImageEmbed from './modules/image-embed.ts';
|
|
9
|
+
const colors = [
|
|
10
|
+
'#000000',
|
|
11
|
+
'#e60000',
|
|
12
|
+
'#ff9900',
|
|
13
|
+
'#ffff00',
|
|
14
|
+
'#008a00',
|
|
15
|
+
'#0066cc',
|
|
16
|
+
'#9933ff',
|
|
17
|
+
'#ffffff',
|
|
18
|
+
'#facccc',
|
|
19
|
+
'#ffebcc',
|
|
20
|
+
'#ffffcc',
|
|
21
|
+
'#cce8cc',
|
|
22
|
+
'#cce0f5',
|
|
23
|
+
'#ebd6ff',
|
|
24
|
+
'#bbbbbb',
|
|
25
|
+
'#f06666',
|
|
26
|
+
'#ffc266',
|
|
27
|
+
'#ffff66',
|
|
28
|
+
'#66b966',
|
|
29
|
+
'#66a3e0',
|
|
30
|
+
'#c285ff',
|
|
31
|
+
'#888888',
|
|
32
|
+
'#a10000',
|
|
33
|
+
'#b26b00',
|
|
34
|
+
'#b2b200',
|
|
35
|
+
'#006100',
|
|
36
|
+
'#0047b2',
|
|
37
|
+
'#6b24b2',
|
|
38
|
+
'#444444',
|
|
39
|
+
'#5c0000',
|
|
40
|
+
'#663d00',
|
|
41
|
+
'#666600',
|
|
42
|
+
'#003700',
|
|
43
|
+
'#002966',
|
|
44
|
+
'#3d1466',
|
|
45
|
+
];
|
|
46
|
+
const reQuillControl = /^ql-/;
|
|
47
|
+
const createTooltip = (input, showDelay = 350) => {
|
|
48
|
+
const isPicker = input instanceof Picker;
|
|
49
|
+
const reference = isPicker ? input.container : input;
|
|
50
|
+
const title = isPicker ? input.select.dataset.title : input.dataset.title;
|
|
51
|
+
tippy(reference, {
|
|
52
|
+
content: title || '',
|
|
53
|
+
appendTo: 'parent',
|
|
54
|
+
placement: 'bottom',
|
|
55
|
+
delay: [showDelay, 0],
|
|
56
|
+
trigger: 'click focus',
|
|
57
|
+
onShow: () => {
|
|
58
|
+
if (reference.classList.contains('ql-expanded'))
|
|
59
|
+
return false;
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
export default () => {
|
|
64
|
+
var _a;
|
|
65
|
+
Quill.register(`modules/${ImageEmbed.NAME}`, ImageEmbed, true);
|
|
66
|
+
const { toolbar } = SnowTheme.DEFAULTS.modules;
|
|
67
|
+
return _a = class TailorTheme extends SnowTheme {
|
|
68
|
+
extendToolbar(toolbar) {
|
|
69
|
+
super.extendToolbar(toolbar);
|
|
70
|
+
this.pickers.forEach((picker) => createTooltip(picker));
|
|
71
|
+
}
|
|
72
|
+
buildButtons(buttons) {
|
|
73
|
+
buttons.forEach((it) => {
|
|
74
|
+
buildButton(it);
|
|
75
|
+
createTooltip(it);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
buildPickers(selects, icons) {
|
|
79
|
+
const filteredSelects = Array.from(selects);
|
|
80
|
+
const background = remove(filteredSelects, (it) => it.classList.contains('ql-background'));
|
|
81
|
+
const color = remove(filteredSelects, (it) => it.classList.contains('ql-color'));
|
|
82
|
+
const align = remove(filteredSelects, (it) => it.classList.contains('ql-align'));
|
|
83
|
+
super.buildPickers(filteredSelects, icons);
|
|
84
|
+
if (background) {
|
|
85
|
+
fillSelect(background, colors, '#ffffff');
|
|
86
|
+
this.pickers.push(new ColorPicker(this.quill, {
|
|
87
|
+
type: 'background',
|
|
88
|
+
select: background,
|
|
89
|
+
label: getMdiIcon('background'),
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
92
|
+
if (color) {
|
|
93
|
+
fillSelect(color, colors, '#000000');
|
|
94
|
+
this.pickers.push(new ColorPicker(this.quill, {
|
|
95
|
+
type: 'color',
|
|
96
|
+
select: color,
|
|
97
|
+
label: getMdiIcon('color'),
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
100
|
+
if (align) {
|
|
101
|
+
const icons = mapKeys(mdiIcons.align, (_key, icon) => getMdiIcon('align', icon));
|
|
102
|
+
this.pickers.push(new IconPicker(align, icons));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
Object.defineProperty(_a, "NAME", {
|
|
107
|
+
enumerable: true,
|
|
108
|
+
configurable: true,
|
|
109
|
+
writable: true,
|
|
110
|
+
value: 'tailor'
|
|
111
|
+
}),
|
|
112
|
+
Object.defineProperty(_a, "DEFAULTS", {
|
|
113
|
+
enumerable: true,
|
|
114
|
+
configurable: true,
|
|
115
|
+
writable: true,
|
|
116
|
+
value: {
|
|
117
|
+
modules: {
|
|
118
|
+
toolbar,
|
|
119
|
+
[ImageEmbed.NAME]: true,
|
|
120
|
+
},
|
|
121
|
+
}
|
|
122
|
+
}),
|
|
123
|
+
_a;
|
|
124
|
+
};
|
|
125
|
+
function buildButton(button) {
|
|
126
|
+
let name = Array.from(button.classList).find((it) => reQuillControl.test(it));
|
|
127
|
+
if (!name)
|
|
128
|
+
return;
|
|
129
|
+
name = name.replace(reQuillControl, '');
|
|
130
|
+
if (name === 'direction') {
|
|
131
|
+
button.innerHTML = getMdiIcon(name, '') + getMdiIcon(name, 'rtl');
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
button.innerHTML = getMdiIcon(name, button.value);
|
|
135
|
+
}
|
|
136
|
+
function fillSelect(select, values, defaultValue = '') {
|
|
137
|
+
values.forEach((value) => {
|
|
138
|
+
const option = document.createElement('option');
|
|
139
|
+
if (value === defaultValue) {
|
|
140
|
+
option.setAttribute('selected', 'selected');
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
option.setAttribute('value', value);
|
|
144
|
+
}
|
|
145
|
+
select.appendChild(option);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
function mapKeys(obj, cb) {
|
|
149
|
+
return Object.entries(obj).reduce((acc, [key, value]) => {
|
|
150
|
+
value = cb(value, key);
|
|
151
|
+
return Object.assign(acc, { [key]: value });
|
|
152
|
+
}, {});
|
|
153
|
+
}
|
|
154
|
+
function remove(arr, cb) {
|
|
155
|
+
const index = arr.findIndex(cb);
|
|
156
|
+
return arr.splice(index, 1)[0];
|
|
157
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Module } from 'quill';
|
|
2
|
+
import ImageEmbedTooltip from '../ui/image-embed-tooltip';
|
|
3
|
+
class ImageEmbed extends Module {
|
|
4
|
+
constructor(quill, options = {}) {
|
|
5
|
+
super(quill, options);
|
|
6
|
+
const { bounds } = quill.options;
|
|
7
|
+
const tooltip = new ImageEmbedTooltip(quill, bounds, options);
|
|
8
|
+
const toolbar = quill.getModule('toolbar');
|
|
9
|
+
toolbar.handlers.image = () => tooltip.show();
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(ImageEmbed, "NAME", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
configurable: true,
|
|
15
|
+
writable: true,
|
|
16
|
+
value: 'imageEmbed'
|
|
17
|
+
});
|
|
18
|
+
export default ImageEmbed;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const isString = (arg) => typeof arg === 'string';
|
|
2
|
+
const mdiIcons = {
|
|
3
|
+
undo: 'undo',
|
|
4
|
+
redo: 'redo',
|
|
5
|
+
align: {
|
|
6
|
+
'': 'format-align-left',
|
|
7
|
+
center: 'format-align-center',
|
|
8
|
+
right: 'format-align-right',
|
|
9
|
+
justify: 'format-align-justify',
|
|
10
|
+
},
|
|
11
|
+
background: 'format-color-highlight',
|
|
12
|
+
blockquote: 'format-quote-close',
|
|
13
|
+
bold: 'format-bold',
|
|
14
|
+
clean: 'format-clear',
|
|
15
|
+
code: 'code-tags',
|
|
16
|
+
'code-block': 'code-tags',
|
|
17
|
+
color: 'format-color-text',
|
|
18
|
+
direction: {
|
|
19
|
+
'': 'format-pilcrow-arrow-right',
|
|
20
|
+
rtl: 'format-pilcrow-arrow-left',
|
|
21
|
+
},
|
|
22
|
+
formula: 'function-variant',
|
|
23
|
+
header: {
|
|
24
|
+
1: 'format-header-1',
|
|
25
|
+
2: 'format-header-2',
|
|
26
|
+
},
|
|
27
|
+
italic: 'format-italic',
|
|
28
|
+
image: 'image-plus',
|
|
29
|
+
indent: {
|
|
30
|
+
'+1': 'format-indent-increase',
|
|
31
|
+
'-1': 'format-indent-decrease',
|
|
32
|
+
},
|
|
33
|
+
link: 'link',
|
|
34
|
+
list: {
|
|
35
|
+
bullet: 'format-list-bulleted',
|
|
36
|
+
check: 'format-list-checks',
|
|
37
|
+
ordered: 'format-list-numbered',
|
|
38
|
+
},
|
|
39
|
+
script: {
|
|
40
|
+
sub: 'format-subscript',
|
|
41
|
+
super: 'format-superscript',
|
|
42
|
+
},
|
|
43
|
+
strike: 'format-strikethrough',
|
|
44
|
+
table: 'table-plus',
|
|
45
|
+
underline: 'format-underline',
|
|
46
|
+
video: 'video-plus',
|
|
47
|
+
};
|
|
48
|
+
export default mdiIcons;
|
|
49
|
+
const textColor = `
|
|
50
|
+
<span style="position: relative;">
|
|
51
|
+
<i class="icon mdi-format-color-text mdi v-icon notranslate v-theme--default"></i>
|
|
52
|
+
<i class="icon mdi-color-helper mdi v-icon notranslate v-theme--default" style="position: absolute; top: 0; left: 0;"></i>
|
|
53
|
+
</span>
|
|
54
|
+
`;
|
|
55
|
+
const bgColor = `
|
|
56
|
+
<span style="position: relative;">
|
|
57
|
+
<i class="icon mdi-format-color-highlight mdi v-icon notranslate v-theme--default"></i>
|
|
58
|
+
<i class="icon mdi-color-helper mdi v-icon notranslate v-theme--default" style="position: absolute; top:0; left: 0;"></i>
|
|
59
|
+
</span>
|
|
60
|
+
`;
|
|
61
|
+
export function getMdiIcon(name, value = '') {
|
|
62
|
+
if (name === 'color')
|
|
63
|
+
return textColor;
|
|
64
|
+
if (name === 'background')
|
|
65
|
+
return bgColor;
|
|
66
|
+
const icon = mdiIcons[name];
|
|
67
|
+
const code = isString(icon) ? icon : icon[value];
|
|
68
|
+
return `<span class="icon mdi mdi-${code}"></span>`;
|
|
69
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import './color-picker.scss';
|
|
2
|
+
import ColorPicker from 'quill/ui/color-picker';
|
|
3
|
+
import Quill from 'quill';
|
|
4
|
+
interface ColorPickerOptions {
|
|
5
|
+
type: string;
|
|
6
|
+
select: HTMLSelectElement;
|
|
7
|
+
label: string;
|
|
8
|
+
}
|
|
9
|
+
export default class ExtendedColorPicker extends ColorPicker {
|
|
10
|
+
options: HTMLElement;
|
|
11
|
+
readonly quill: Quill;
|
|
12
|
+
readonly type: string;
|
|
13
|
+
constructor(quill: Quill, options: ColorPickerOptions);
|
|
14
|
+
buildPicker(): void;
|
|
15
|
+
}
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import './color-picker.scss';
|
|
2
|
+
import ColorPicker from 'quill/ui/color-picker';
|
|
3
|
+
import Quill from 'quill';
|
|
4
|
+
const className = (...names) => names.join(' ');
|
|
5
|
+
export default class ExtendedColorPicker extends ColorPicker {
|
|
6
|
+
constructor(quill, options) {
|
|
7
|
+
super(options.select, options.label);
|
|
8
|
+
Object.defineProperty(this, "quill", {
|
|
9
|
+
enumerable: true,
|
|
10
|
+
configurable: true,
|
|
11
|
+
writable: true,
|
|
12
|
+
value: void 0
|
|
13
|
+
});
|
|
14
|
+
Object.defineProperty(this, "type", {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
configurable: true,
|
|
17
|
+
writable: true,
|
|
18
|
+
value: void 0
|
|
19
|
+
});
|
|
20
|
+
this.quill = quill;
|
|
21
|
+
this.type = options.type;
|
|
22
|
+
}
|
|
23
|
+
buildPicker() {
|
|
24
|
+
super.buildPicker();
|
|
25
|
+
const btnReset = createButton({ icon: 'mdi-water-off', text: 'None' });
|
|
26
|
+
btnReset.className = className('picker-item__none');
|
|
27
|
+
btnReset.addEventListener('click', () => {
|
|
28
|
+
this.quill.format(this.type, null, Quill.sources.USER);
|
|
29
|
+
this.close();
|
|
30
|
+
});
|
|
31
|
+
const colorOptions = wrapOptions(this.options);
|
|
32
|
+
colorOptions.className = className('picker-colors');
|
|
33
|
+
this.options.appendChild(btnReset);
|
|
34
|
+
this.options.appendChild(colorOptions);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function wrapOptions({ children }) {
|
|
38
|
+
const container = document.createElement('div');
|
|
39
|
+
Array.from(children).forEach((option) => container.appendChild(option));
|
|
40
|
+
return container;
|
|
41
|
+
}
|
|
42
|
+
function createButton({ icon, text }) {
|
|
43
|
+
const btn = document.createElement('span');
|
|
44
|
+
btn.tabIndex = 0;
|
|
45
|
+
btn.setAttribute('role', 'button');
|
|
46
|
+
btn.appendChild(createIcon(icon));
|
|
47
|
+
btn.innerHTML += text;
|
|
48
|
+
return btn;
|
|
49
|
+
}
|
|
50
|
+
function createIcon(name) {
|
|
51
|
+
const icon = document.createElement('span');
|
|
52
|
+
icon.className = className('icon', 'mdi', name);
|
|
53
|
+
return icon;
|
|
54
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import './image-embed-tooltip.scss';
|
|
2
|
+
import Quill from 'quill';
|
|
3
|
+
import Tooltip from './tooltip.ts';
|
|
4
|
+
export default class ImageEmbedTooltip extends Tooltip {
|
|
5
|
+
static TEMPLATE: string;
|
|
6
|
+
readonly padding: string;
|
|
7
|
+
readonly textbox: HTMLInputElement | null;
|
|
8
|
+
readonly btnAction: HTMLAnchorElement | null;
|
|
9
|
+
constructor(quill: Quill, bounds: HTMLElement, { spacing }?: {
|
|
10
|
+
spacing?: number | undefined;
|
|
11
|
+
});
|
|
12
|
+
listen(): void;
|
|
13
|
+
show(): void;
|
|
14
|
+
embedImage(): void;
|
|
15
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import './image-embed-tooltip.scss';
|
|
2
|
+
import Keyboard from 'quill/modules/keyboard';
|
|
3
|
+
import Quill from 'quill';
|
|
4
|
+
import Tooltip from './tooltip.ts';
|
|
5
|
+
class ImageEmbedTooltip extends Tooltip {
|
|
6
|
+
constructor(quill, bounds, { spacing = 0 } = {}) {
|
|
7
|
+
super(quill, bounds);
|
|
8
|
+
Object.defineProperty(this, "padding", {
|
|
9
|
+
enumerable: true,
|
|
10
|
+
configurable: true,
|
|
11
|
+
writable: true,
|
|
12
|
+
value: void 0
|
|
13
|
+
});
|
|
14
|
+
Object.defineProperty(this, "textbox", {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
configurable: true,
|
|
17
|
+
writable: true,
|
|
18
|
+
value: void 0
|
|
19
|
+
});
|
|
20
|
+
Object.defineProperty(this, "btnAction", {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
configurable: true,
|
|
23
|
+
writable: true,
|
|
24
|
+
value: void 0
|
|
25
|
+
});
|
|
26
|
+
this.padding = ' '.repeat(spacing);
|
|
27
|
+
this.textbox = this.root.querySelector('input.url');
|
|
28
|
+
this.btnAction = this.root.querySelector('a.action');
|
|
29
|
+
this.listen();
|
|
30
|
+
}
|
|
31
|
+
listen() {
|
|
32
|
+
this.btnAction?.addEventListener('click', () => this.embedImage());
|
|
33
|
+
this.textbox?.addEventListener('keydown', (e) => {
|
|
34
|
+
if (Keyboard.match(e, { key: 'enter' })) {
|
|
35
|
+
this.embedImage();
|
|
36
|
+
e.preventDefault();
|
|
37
|
+
}
|
|
38
|
+
else if (Keyboard.match(e, { key: 'escape' })) {
|
|
39
|
+
this.hide();
|
|
40
|
+
e.preventDefault();
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
show() {
|
|
45
|
+
super.show();
|
|
46
|
+
this.root.classList.add('ql-image-embed');
|
|
47
|
+
this.textbox?.select();
|
|
48
|
+
}
|
|
49
|
+
embedImage() {
|
|
50
|
+
const url = this.textbox?.value;
|
|
51
|
+
if (!url)
|
|
52
|
+
return;
|
|
53
|
+
const { quill } = this;
|
|
54
|
+
const range = quill.getSelection(true);
|
|
55
|
+
if (range) {
|
|
56
|
+
const { padding } = this;
|
|
57
|
+
const source = Quill.sources.USER;
|
|
58
|
+
let offset = range.index + range.length;
|
|
59
|
+
quill.insertText(offset, padding, source);
|
|
60
|
+
offset += padding.length;
|
|
61
|
+
quill.insertEmbed(offset, 'image', url, source);
|
|
62
|
+
offset += 1;
|
|
63
|
+
quill.insertText(offset, padding, source);
|
|
64
|
+
offset += padding.length;
|
|
65
|
+
quill.setSelection(offset, source);
|
|
66
|
+
}
|
|
67
|
+
if (this.textbox)
|
|
68
|
+
this.textbox.value = '';
|
|
69
|
+
this.hide();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
Object.defineProperty(ImageEmbedTooltip, "TEMPLATE", {
|
|
73
|
+
enumerable: true,
|
|
74
|
+
configurable: true,
|
|
75
|
+
writable: true,
|
|
76
|
+
value: `
|
|
77
|
+
<div class="controls">
|
|
78
|
+
<label>Enter image url:</label>
|
|
79
|
+
<input class="url" type="text">
|
|
80
|
+
<a class="action">Save</a>
|
|
81
|
+
</div>
|
|
82
|
+
`
|
|
83
|
+
});
|
|
84
|
+
export default ImageEmbedTooltip;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import Tooltip from 'quill/ui/tooltip';
|
|
2
|
+
export default class ExtendedTooltip extends Tooltip {
|
|
3
|
+
constructor(quill, bounds) {
|
|
4
|
+
super(quill, bounds);
|
|
5
|
+
Object.defineProperty(this, "isOpen", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
writable: true,
|
|
9
|
+
value: void 0
|
|
10
|
+
});
|
|
11
|
+
this._onClick = this._onClick.bind(this);
|
|
12
|
+
this.isOpen = false;
|
|
13
|
+
}
|
|
14
|
+
show() {
|
|
15
|
+
super.show();
|
|
16
|
+
this.isOpen = true;
|
|
17
|
+
setTimeout(() => document.body.addEventListener('mousedown', (e) => this._onClick(e)), 0);
|
|
18
|
+
const bounds = this.quill.getBounds(this.quill.selection.savedRange);
|
|
19
|
+
if (bounds)
|
|
20
|
+
this.position(bounds);
|
|
21
|
+
}
|
|
22
|
+
hide() {
|
|
23
|
+
super.hide();
|
|
24
|
+
this.isOpen = false;
|
|
25
|
+
document.body.removeEventListener('mousedown', (e) => this._onClick(e));
|
|
26
|
+
}
|
|
27
|
+
_onClick(e) {
|
|
28
|
+
if (this.isOpen && e.target && !this.root.contains(e.target))
|
|
29
|
+
this.hide();
|
|
30
|
+
}
|
|
31
|
+
}
|