quill-table-up 2.0.2 → 2.0.3
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/index.d.ts +3 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/table-creator.css +1 -1
- package/package.json +9 -14
- package/src/__tests__/e2e/custom-creator.test.ts +44 -0
- package/src/__tests__/e2e/table-align.test.ts +39 -0
- package/src/__tests__/e2e/table-resize.test.ts +152 -0
- package/src/__tests__/e2e/table-scrollbar.test.ts +31 -0
- package/src/__tests__/e2e/table-selection.test.ts +83 -0
- package/src/__tests__/e2e/utils.ts +6 -0
- package/src/__tests__/unit/table-insert-blot.test.ts +464 -0
- package/src/__tests__/unit/table-insert-remove-merge.test.ts +1270 -0
- package/src/__tests__/unit/table-redo-undo.test.ts +909 -0
- package/src/__tests__/unit/utils.test-d.ts +49 -0
- package/src/__tests__/unit/utils.test.ts +715 -0
- package/src/__tests__/unit/utils.ts +216 -0
- package/src/__tests__/unit/vitest.d.ts +12 -0
- package/src/formats/container-format.ts +52 -0
- package/src/formats/index.ts +10 -0
- package/src/formats/overrides/block.ts +93 -0
- package/src/formats/overrides/blockquote.ts +8 -0
- package/src/formats/overrides/code.ts +8 -0
- package/src/formats/overrides/header.ts +8 -0
- package/src/formats/overrides/index.ts +6 -0
- package/src/formats/overrides/list.ts +10 -0
- package/src/formats/overrides/scroll.ts +51 -0
- package/src/formats/table-body-format.ts +92 -0
- package/src/formats/table-cell-format.ts +139 -0
- package/src/formats/table-cell-inner-format.ts +251 -0
- package/src/formats/table-col-format.ts +174 -0
- package/src/formats/table-colgroup-format.ts +133 -0
- package/src/formats/table-main-format.ts +143 -0
- package/src/formats/table-row-format.ts +147 -0
- package/src/formats/table-wrapper-format.ts +55 -0
- package/src/formats/utils.ts +3 -0
- package/src/index.ts +1157 -0
- package/src/modules/index.ts +5 -0
- package/src/modules/table-align.ts +116 -0
- package/src/modules/table-menu/constants.ts +140 -0
- package/src/modules/table-menu/index.ts +3 -0
- package/src/modules/table-menu/table-menu-common.ts +249 -0
- package/src/modules/table-menu/table-menu-contextmenu.ts +94 -0
- package/src/modules/table-menu/table-menu-select.ts +28 -0
- package/src/modules/table-resize/index.ts +5 -0
- package/src/modules/table-resize/table-resize-box.ts +293 -0
- package/src/modules/table-resize/table-resize-common.ts +343 -0
- package/src/modules/table-resize/table-resize-line.ts +163 -0
- package/src/modules/table-resize/table-resize-scale.ts +154 -0
- package/src/modules/table-resize/utils.ts +3 -0
- package/src/modules/table-scrollbar.ts +255 -0
- package/src/modules/table-selection.ts +262 -0
- package/src/style/button.less +45 -0
- package/src/style/color-picker.less +134 -0
- package/src/style/dialog.less +53 -0
- package/src/style/functions.less +9 -0
- package/src/style/index.less +89 -0
- package/src/style/input.less +64 -0
- package/src/style/select-box.less +51 -0
- package/src/style/table-creator.less +68 -0
- package/src/style/table-menu.less +122 -0
- package/src/style/table-resize-scale.less +31 -0
- package/src/style/table-resize.less +183 -0
- package/src/style/table-scrollbar.less +49 -0
- package/src/style/table-selection.less +15 -0
- package/src/style/tooltip.less +19 -0
- package/src/style/variables.less +1 -0
- package/src/svg/background.svg +1 -0
- package/src/svg/border.svg +1 -0
- package/src/svg/color.svg +1 -0
- package/src/svg/insert-bottom.svg +1 -0
- package/src/svg/insert-left.svg +1 -0
- package/src/svg/insert-right.svg +1 -0
- package/src/svg/insert-top.svg +1 -0
- package/src/svg/merge-cell.svg +1 -0
- package/src/svg/remove-column.svg +1 -0
- package/src/svg/remove-row.svg +1 -0
- package/src/svg/remove-table.svg +1 -0
- package/src/svg/split-cell.svg +1 -0
- package/src/types.d.ts +4 -0
- package/src/utils/bem.ts +23 -0
- package/src/utils/color.ts +109 -0
- package/src/utils/components/button.ts +22 -0
- package/src/utils/components/color-picker.ts +236 -0
- package/src/utils/components/dialog.ts +41 -0
- package/src/utils/components/index.ts +6 -0
- package/src/utils/components/input.ts +74 -0
- package/src/utils/components/table/creator.ts +86 -0
- package/src/utils/components/table/index.ts +2 -0
- package/src/utils/components/table/select-box.ts +83 -0
- package/src/utils/components/tooltip.ts +186 -0
- package/src/utils/constants.ts +99 -0
- package/src/utils/index.ts +7 -0
- package/src/utils/is.ts +6 -0
- package/src/utils/position.ts +21 -0
- package/src/utils/types.ts +131 -0
- package/src/utils/utils.ts +139 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import type { HSB } from '../color';
|
|
2
|
+
import { createBEM } from '../bem';
|
|
3
|
+
import { HEXtoRGB, HSBtoHEX, HSBtoRGB, RGBtoHEX, RGBtoHSB, validateHSB } from '../color';
|
|
4
|
+
|
|
5
|
+
interface ColorPickerOptions {
|
|
6
|
+
color: string;
|
|
7
|
+
onChange: (color: string) => void;
|
|
8
|
+
};
|
|
9
|
+
export const createColorPicker = (options: Partial<ColorPickerOptions> = {}) => {
|
|
10
|
+
const contentWidth = 230;
|
|
11
|
+
const contentHeight = 150;
|
|
12
|
+
const handleSizeSec = 10;
|
|
13
|
+
|
|
14
|
+
let hsbValue: HSB = RGBtoHSB(HEXtoRGB(options.color || '#ff0000'));
|
|
15
|
+
const bem = createBEM('color-picker');
|
|
16
|
+
const root = document.createElement('div');
|
|
17
|
+
root.classList.add(bem.b());
|
|
18
|
+
|
|
19
|
+
const content = document.createElement('div');
|
|
20
|
+
content.classList.add(bem.be('content'));
|
|
21
|
+
|
|
22
|
+
const colorSelector = document.createElement('div');
|
|
23
|
+
colorSelector.classList.add(bem.be('selector'));
|
|
24
|
+
|
|
25
|
+
const colorBackground = document.createElement('div');
|
|
26
|
+
colorBackground.classList.add(bem.be('background'));
|
|
27
|
+
colorSelector.appendChild(colorBackground);
|
|
28
|
+
|
|
29
|
+
const colorHandle = document.createElement('div');
|
|
30
|
+
colorHandle.classList.add(bem.be('background-handle'));
|
|
31
|
+
colorBackground.appendChild(colorHandle);
|
|
32
|
+
|
|
33
|
+
const colorAlpha = document.createElement('div');
|
|
34
|
+
colorAlpha.classList.add(bem.be('alpha'));
|
|
35
|
+
|
|
36
|
+
const alphaBg = document.createElement('div');
|
|
37
|
+
alphaBg.classList.add(bem.be('alpha-bg'));
|
|
38
|
+
|
|
39
|
+
const alphaHandle = document.createElement('div');
|
|
40
|
+
alphaHandle.classList.add(bem.be('alpha-handle'));
|
|
41
|
+
|
|
42
|
+
colorAlpha.appendChild(alphaBg);
|
|
43
|
+
colorAlpha.appendChild(alphaHandle);
|
|
44
|
+
|
|
45
|
+
const colorHue = document.createElement('div');
|
|
46
|
+
colorHue.classList.add(bem.be('hue'));
|
|
47
|
+
|
|
48
|
+
const colorHueHandle = document.createElement('div');
|
|
49
|
+
colorHueHandle.classList.add(bem.be('hue-handle'));
|
|
50
|
+
colorHue.appendChild(colorHueHandle);
|
|
51
|
+
|
|
52
|
+
const action = document.createElement('div');
|
|
53
|
+
action.classList.add(bem.be('action'));
|
|
54
|
+
|
|
55
|
+
const [colorRInput, colorGInput, colorBInput, colorAInput] = (['r', 'g', 'b', 'a'] as const).map((key) => {
|
|
56
|
+
const item = document.createElement('div');
|
|
57
|
+
item.classList.add(bem.be('action-item'), key);
|
|
58
|
+
|
|
59
|
+
const label = document.createElement('label');
|
|
60
|
+
label.textContent = key.toUpperCase();
|
|
61
|
+
|
|
62
|
+
const colorInput = document.createElement('input');
|
|
63
|
+
colorInput.classList.add(bem.be('input'));
|
|
64
|
+
|
|
65
|
+
colorInput.addEventListener('input', () => {
|
|
66
|
+
colorInput.value = colorInput.value.replaceAll(/[^0-9]/g, '');
|
|
67
|
+
});
|
|
68
|
+
colorInput.addEventListener('change', () => {
|
|
69
|
+
let value = Math.round(Number(colorInput.value));
|
|
70
|
+
if (key === 'a') {
|
|
71
|
+
value = value / 100;
|
|
72
|
+
}
|
|
73
|
+
const result = validateHSB(RGBtoHSB(Object.assign({}, HSBtoRGB(hsbValue), { [key]: value })));
|
|
74
|
+
updateValue(result);
|
|
75
|
+
updateUI();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
item.appendChild(label);
|
|
79
|
+
item.appendChild(colorInput);
|
|
80
|
+
action.appendChild(item);
|
|
81
|
+
|
|
82
|
+
return colorInput;
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
content.appendChild(colorHue);
|
|
86
|
+
content.appendChild(colorSelector);
|
|
87
|
+
content.appendChild(colorAlpha);
|
|
88
|
+
root.appendChild(content);
|
|
89
|
+
root.appendChild(action);
|
|
90
|
+
|
|
91
|
+
let colorDragging = false;
|
|
92
|
+
let hueDragging = false;
|
|
93
|
+
let alphaDragging = false;
|
|
94
|
+
|
|
95
|
+
function updateInput() {
|
|
96
|
+
const hex = HSBtoHEX(hsbValue);
|
|
97
|
+
for (const [i, input] of [colorRInput, colorGInput, colorBInput].entries()) {
|
|
98
|
+
input.value = String(Number.parseInt(hex[i * 2] + hex[i * 2 + 1], 16));
|
|
99
|
+
}
|
|
100
|
+
colorAInput.value = String((hsbValue.a * 100).toFixed(0));
|
|
101
|
+
}
|
|
102
|
+
function updateColorHandle() {
|
|
103
|
+
Object.assign(colorHandle.style, {
|
|
104
|
+
left: `${Math.floor((contentWidth * hsbValue.s) / 100)}px`,
|
|
105
|
+
top: `${Math.floor((contentHeight * (100 - hsbValue.b)) / 100)}px`,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
function updateColorSelector() {
|
|
109
|
+
colorSelector.style.backgroundColor = `#${RGBtoHEX(HSBtoRGB({
|
|
110
|
+
h: hsbValue.h,
|
|
111
|
+
s: 100,
|
|
112
|
+
b: 100,
|
|
113
|
+
a: 1,
|
|
114
|
+
}))}`;
|
|
115
|
+
}
|
|
116
|
+
function updateHue() {
|
|
117
|
+
colorHueHandle.style.top = `${Math.floor(contentHeight - (contentHeight * hsbValue.h) / 360)}px`;
|
|
118
|
+
}
|
|
119
|
+
function updateAlphaHandle() {
|
|
120
|
+
alphaHandle.style.left = `${hsbValue.a * 100}%`;
|
|
121
|
+
}
|
|
122
|
+
function updateAlphaBg() {
|
|
123
|
+
const { r, g, b } = HSBtoRGB(hsbValue);
|
|
124
|
+
alphaBg.style.background = `linear-gradient(to right, rgba(${r}, ${g}, ${b}, 0) 0%, rgba(${r}, ${g}, ${b}, 1) 100%)`;
|
|
125
|
+
}
|
|
126
|
+
function updateUI() {
|
|
127
|
+
updateColorHandle();
|
|
128
|
+
updateColorSelector();
|
|
129
|
+
updateHue();
|
|
130
|
+
updateAlphaHandle();
|
|
131
|
+
updateAlphaBg();
|
|
132
|
+
updateInput();
|
|
133
|
+
}
|
|
134
|
+
function updateValue(value: Partial<HSB>) {
|
|
135
|
+
hsbValue = validateHSB(Object.assign({}, hsbValue, value));
|
|
136
|
+
|
|
137
|
+
updateInput();
|
|
138
|
+
|
|
139
|
+
if (options.onChange) {
|
|
140
|
+
options.onChange(`#${HSBtoHEX(hsbValue)}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function pickColor(event: MouseEvent) {
|
|
145
|
+
const rect = colorSelector.getBoundingClientRect();
|
|
146
|
+
const top = rect.top + (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0);
|
|
147
|
+
const left = rect.left + document.body.scrollLeft;
|
|
148
|
+
const saturation = Math.floor((100 * Math.max(0, Math.min(contentWidth, event.pageX - left))) / contentWidth);
|
|
149
|
+
const brightness = Math.floor((100 * (contentHeight - Math.max(0, Math.min(contentHeight, event.pageY - top)))) / contentHeight);
|
|
150
|
+
|
|
151
|
+
updateValue({
|
|
152
|
+
s: saturation,
|
|
153
|
+
b: brightness,
|
|
154
|
+
});
|
|
155
|
+
updateUI();
|
|
156
|
+
}
|
|
157
|
+
function pickHue(event: MouseEvent) {
|
|
158
|
+
const top = colorHue.getBoundingClientRect().top + (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0);
|
|
159
|
+
|
|
160
|
+
updateValue({
|
|
161
|
+
h: Math.floor((360 * (contentHeight - Math.max(0, Math.min(contentHeight, event.pageY - top)))) / contentHeight),
|
|
162
|
+
});
|
|
163
|
+
updateUI();
|
|
164
|
+
}
|
|
165
|
+
function pickAlpha(event: MouseEvent) {
|
|
166
|
+
const { pageX } = event;
|
|
167
|
+
const rect = colorAlpha.getBoundingClientRect();
|
|
168
|
+
let left = pageX - rect.left;
|
|
169
|
+
left = Math.max(handleSizeSec / 2, left);
|
|
170
|
+
left = Math.min(left, rect.width - handleSizeSec / 2);
|
|
171
|
+
|
|
172
|
+
updateValue({
|
|
173
|
+
a: Math.round(((left - 10 / 2) / (rect.width - 10)) * 100) / 100,
|
|
174
|
+
});
|
|
175
|
+
updateUI();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function onDrag(event: MouseEvent) {
|
|
179
|
+
if (colorDragging) {
|
|
180
|
+
event.preventDefault();
|
|
181
|
+
pickColor(event);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (hueDragging) {
|
|
185
|
+
event.preventDefault();
|
|
186
|
+
pickHue(event);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (alphaDragging) {
|
|
190
|
+
event.preventDefault();
|
|
191
|
+
pickAlpha(event);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function onColorSelectorDragEnd() {
|
|
196
|
+
document.removeEventListener('mousemove', onDrag);
|
|
197
|
+
document.removeEventListener('mouseup', onColorSelectorDragEnd);
|
|
198
|
+
colorDragging = false;
|
|
199
|
+
}
|
|
200
|
+
function onColorSelectorMousedown(e: MouseEvent) {
|
|
201
|
+
document.addEventListener('mousemove', onDrag);
|
|
202
|
+
document.addEventListener('mouseup', onColorSelectorDragEnd);
|
|
203
|
+
colorDragging = true;
|
|
204
|
+
pickColor(e);
|
|
205
|
+
}
|
|
206
|
+
colorSelector.addEventListener('mousedown', onColorSelectorMousedown);
|
|
207
|
+
|
|
208
|
+
function onColorHueDragEnd() {
|
|
209
|
+
document.removeEventListener('mousemove', onDrag);
|
|
210
|
+
document.removeEventListener('mouseup', onColorHueDragEnd);
|
|
211
|
+
hueDragging = false;
|
|
212
|
+
}
|
|
213
|
+
function onColorHueMousedown(event: MouseEvent) {
|
|
214
|
+
document.addEventListener('mousemove', onDrag);
|
|
215
|
+
document.addEventListener('mouseup', onColorHueDragEnd);
|
|
216
|
+
hueDragging = true;
|
|
217
|
+
pickHue(event);
|
|
218
|
+
}
|
|
219
|
+
colorHue.addEventListener('mousedown', onColorHueMousedown);
|
|
220
|
+
|
|
221
|
+
function onColorAlphaDragEnd() {
|
|
222
|
+
document.removeEventListener('mousemove', onDrag);
|
|
223
|
+
document.removeEventListener('mouseup', onColorAlphaDragEnd);
|
|
224
|
+
alphaDragging = false;
|
|
225
|
+
}
|
|
226
|
+
function onColorAlphaMousedown(event: MouseEvent) {
|
|
227
|
+
document.addEventListener('mousemove', onDrag);
|
|
228
|
+
document.addEventListener('mouseup', onColorAlphaDragEnd);
|
|
229
|
+
alphaDragging = true;
|
|
230
|
+
pickAlpha(event);
|
|
231
|
+
}
|
|
232
|
+
colorAlpha.addEventListener('mousedown', onColorAlphaMousedown);
|
|
233
|
+
|
|
234
|
+
updateUI();
|
|
235
|
+
return root;
|
|
236
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { createBEM } from '../bem';
|
|
2
|
+
|
|
3
|
+
interface DialogOptions {
|
|
4
|
+
child?: HTMLElement;
|
|
5
|
+
target?: HTMLElement;
|
|
6
|
+
beforeClose?: () => void;
|
|
7
|
+
}
|
|
8
|
+
let zindex = 8000;
|
|
9
|
+
export const createDialog = ({ child, target = document.body, beforeClose = () => {} }: DialogOptions = {}) => {
|
|
10
|
+
const bem = createBEM('dialog');
|
|
11
|
+
const appendTo = target;
|
|
12
|
+
const dialog = document.createElement('div');
|
|
13
|
+
dialog.classList.add(bem.b());
|
|
14
|
+
dialog.style.zIndex = String(zindex);
|
|
15
|
+
const overlay = document.createElement('div');
|
|
16
|
+
overlay.classList.add(bem.be('overlay'));
|
|
17
|
+
dialog.appendChild(overlay);
|
|
18
|
+
if (child) {
|
|
19
|
+
const content = document.createElement('div');
|
|
20
|
+
content.classList.add(bem.be('content'));
|
|
21
|
+
content.appendChild(child);
|
|
22
|
+
overlay.appendChild(content);
|
|
23
|
+
content.addEventListener('click', (e) => {
|
|
24
|
+
e.stopPropagation();
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const originOverflow = getComputedStyle(appendTo).overflow;
|
|
29
|
+
appendTo.style.overflow = 'hidden';
|
|
30
|
+
|
|
31
|
+
appendTo.appendChild(dialog);
|
|
32
|
+
const close = () => {
|
|
33
|
+
beforeClose();
|
|
34
|
+
dialog.remove();
|
|
35
|
+
appendTo.style.overflow = originOverflow;
|
|
36
|
+
};
|
|
37
|
+
dialog.addEventListener('click', close);
|
|
38
|
+
zindex += 1;
|
|
39
|
+
|
|
40
|
+
return { dialog, close };
|
|
41
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { createBEM } from '../bem';
|
|
2
|
+
|
|
3
|
+
interface InputOptions {
|
|
4
|
+
type?: string;
|
|
5
|
+
value?: string;
|
|
6
|
+
max?: number;
|
|
7
|
+
min?: number;
|
|
8
|
+
[key: string]: any;
|
|
9
|
+
};
|
|
10
|
+
export const createInputItem = (label: string, options: InputOptions) => {
|
|
11
|
+
const bem = createBEM('input');
|
|
12
|
+
options.type || (options.type = 'text');
|
|
13
|
+
options.value || (options.value = '');
|
|
14
|
+
|
|
15
|
+
const inputItem = document.createElement('div');
|
|
16
|
+
inputItem.classList.add(bem.be('item'));
|
|
17
|
+
|
|
18
|
+
if (label) {
|
|
19
|
+
const inputLabel = document.createElement('span');
|
|
20
|
+
inputLabel.classList.add(bem.be('label'));
|
|
21
|
+
inputLabel.textContent = label;
|
|
22
|
+
inputItem.appendChild(inputLabel);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const inputInput = document.createElement('div');
|
|
26
|
+
inputInput.classList.add(bem.be('input'));
|
|
27
|
+
const input = document.createElement('input');
|
|
28
|
+
for (const key in options) {
|
|
29
|
+
input.setAttribute(key, options[key]);
|
|
30
|
+
}
|
|
31
|
+
if (options.max || options.min) {
|
|
32
|
+
input.addEventListener('blur', () => {
|
|
33
|
+
if (options.max && options.max <= Number(input.value)) {
|
|
34
|
+
input.value = String(options.max);
|
|
35
|
+
}
|
|
36
|
+
if (options.min && options.min >= Number(input.value)) {
|
|
37
|
+
input.value = String(options.min);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
inputInput.appendChild(input);
|
|
43
|
+
inputItem.appendChild(inputInput);
|
|
44
|
+
|
|
45
|
+
input.addEventListener('focus', () => {
|
|
46
|
+
inputInput.classList.add('focus');
|
|
47
|
+
});
|
|
48
|
+
input.addEventListener('blur', () => {
|
|
49
|
+
inputInput.classList.remove('focus');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const errorTip = (msg: string) => {
|
|
53
|
+
let errorTip: HTMLElement;
|
|
54
|
+
if (inputInput.classList.contains('error')) {
|
|
55
|
+
errorTip = inputInput.querySelector(`.${bem.be('error-tip')}`)!;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
errorTip = document.createElement('span');
|
|
59
|
+
errorTip.classList.add(bem.be('error-tip'));
|
|
60
|
+
inputInput.appendChild(errorTip);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
errorTip.textContent = msg;
|
|
64
|
+
inputInput.classList.add('error');
|
|
65
|
+
|
|
66
|
+
const removeError = () => {
|
|
67
|
+
inputInput.classList.remove('error');
|
|
68
|
+
errorTip.remove();
|
|
69
|
+
};
|
|
70
|
+
return { removeError };
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
return { item: inputItem, input, errorTip };
|
|
74
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { TableCreatorTextOptions } from '../../types';
|
|
2
|
+
import { createBEM } from '../../bem';
|
|
3
|
+
import { createButton } from '../button';
|
|
4
|
+
import { createDialog } from '../dialog';
|
|
5
|
+
import { createInputItem } from '../input';
|
|
6
|
+
|
|
7
|
+
interface TableCreatorOptions extends Omit<TableCreatorTextOptions, 'customBtnText'> {
|
|
8
|
+
row: number;
|
|
9
|
+
col: number;
|
|
10
|
+
}
|
|
11
|
+
export const showTableCreator = async (options: Partial<TableCreatorOptions> = {}) => {
|
|
12
|
+
const bem = createBEM('creator');
|
|
13
|
+
const box = document.createElement('div');
|
|
14
|
+
box.classList.add(bem.b());
|
|
15
|
+
const inputContent = document.createElement('div');
|
|
16
|
+
inputContent.classList.add(bem.be('input'));
|
|
17
|
+
|
|
18
|
+
const {
|
|
19
|
+
item: rowItem,
|
|
20
|
+
input: rowInput,
|
|
21
|
+
errorTip: rowErrorTip,
|
|
22
|
+
} = createInputItem(options.rowText || 'Row', { type: 'number', value: String(options.row || ''), max: 99 });
|
|
23
|
+
const {
|
|
24
|
+
item: colItem,
|
|
25
|
+
input: colInput,
|
|
26
|
+
errorTip: colErrorTip,
|
|
27
|
+
} = createInputItem(options.colText || 'Column', { type: 'number', value: String(options.col || ''), max: 99 });
|
|
28
|
+
|
|
29
|
+
inputContent.appendChild(rowItem);
|
|
30
|
+
inputContent.appendChild(colItem);
|
|
31
|
+
box.appendChild(inputContent);
|
|
32
|
+
|
|
33
|
+
const control = document.createElement('div');
|
|
34
|
+
control.classList.add(bem.be('control'));
|
|
35
|
+
|
|
36
|
+
const confirmBtn = createButton({ type: 'confirm', content: options.confirmText || 'Confirm' });
|
|
37
|
+
const cancelBtn = createButton({ type: 'default', content: options.cancelText || 'Cancel' });
|
|
38
|
+
|
|
39
|
+
control.appendChild(confirmBtn);
|
|
40
|
+
control.appendChild(cancelBtn);
|
|
41
|
+
box.appendChild(control);
|
|
42
|
+
|
|
43
|
+
const validateInput = (row: number = Number(rowInput.value), col: number = Number(colInput.value)) => {
|
|
44
|
+
if (Number.isNaN(row) || row <= 0) {
|
|
45
|
+
rowErrorTip(options.notPositiveNumberError || 'Please enter a positive integer');
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (Number.isNaN(col) || col <= 0) {
|
|
49
|
+
colErrorTip(options.notPositiveNumberError || 'Please enter a positive integer');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
return { row, col };
|
|
53
|
+
};
|
|
54
|
+
const keyboardClose = (e: KeyboardEvent) => {
|
|
55
|
+
if (e.key === 'Escape') {
|
|
56
|
+
close();
|
|
57
|
+
document.removeEventListener('keydown', keyboardClose);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
return new Promise<{ row: number; col: number }>((resolve, reject) => {
|
|
62
|
+
const { close } = createDialog({ child: box, beforeClose: reject });
|
|
63
|
+
rowInput.focus();
|
|
64
|
+
|
|
65
|
+
for (const input of [rowInput, colInput]) {
|
|
66
|
+
input.addEventListener('keydown', (e) => {
|
|
67
|
+
if (e.key === 'Enter') {
|
|
68
|
+
const result = validateInput();
|
|
69
|
+
if (result) {
|
|
70
|
+
resolve(result);
|
|
71
|
+
close();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
confirmBtn.addEventListener('click', async () => {
|
|
77
|
+
const result = validateInput();
|
|
78
|
+
if (result) {
|
|
79
|
+
resolve(result);
|
|
80
|
+
close();
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
document.addEventListener('keydown', keyboardClose);
|
|
84
|
+
cancelBtn.addEventListener('click', close);
|
|
85
|
+
});
|
|
86
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { TableCreatorTextOptions } from '../../types';
|
|
2
|
+
import { createBEM } from '../../bem';
|
|
3
|
+
import { showTableCreator } from './creator';
|
|
4
|
+
|
|
5
|
+
interface TableSelectOptions {
|
|
6
|
+
row: number;
|
|
7
|
+
col: number;
|
|
8
|
+
onSelect: (row: number, col: number) => void;
|
|
9
|
+
customBtn: boolean;
|
|
10
|
+
texts: Partial<TableCreatorTextOptions>;
|
|
11
|
+
}
|
|
12
|
+
export const createSelectBox = (options: Partial<TableSelectOptions> = {}) => {
|
|
13
|
+
const bem = createBEM('select-box');
|
|
14
|
+
const selectDom = document.createElement('div');
|
|
15
|
+
selectDom.classList.add(bem.b());
|
|
16
|
+
|
|
17
|
+
const selectBlock = document.createElement('div');
|
|
18
|
+
selectBlock.classList.add(bem.be('block'));
|
|
19
|
+
for (let r = 0; r < (options.row || 8); r++) {
|
|
20
|
+
for (let c = 0; c < (options.col || 8); c++) {
|
|
21
|
+
const selectItem = document.createElement('div');
|
|
22
|
+
selectItem.classList.add(bem.be('item'));
|
|
23
|
+
selectItem.dataset.row = String(r + 1);
|
|
24
|
+
selectItem.dataset.col = String(c + 1);
|
|
25
|
+
selectBlock.appendChild(selectItem);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const updateSelectBlockItems = () => {
|
|
29
|
+
const { row, col } = selectDom.dataset;
|
|
30
|
+
for (const item of Array.from(selectBlock.querySelectorAll('.active'))) {
|
|
31
|
+
item.classList.remove('active');
|
|
32
|
+
}
|
|
33
|
+
if (!row || !col) return;
|
|
34
|
+
const childs = Array.from(selectBlock.children) as HTMLElement[];
|
|
35
|
+
for (let i = 0; i < childs.length; i++) {
|
|
36
|
+
const { row: childRow, col: childCol } = childs[i].dataset;
|
|
37
|
+
if (childRow! > row && childCol! > col) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (childRow! <= row && childCol! <= col) {
|
|
41
|
+
childs[i].classList.add('active');
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
childs[i].classList.remove('active');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
selectBlock.addEventListener('mousemove', (e) => {
|
|
49
|
+
if (!e.target) return;
|
|
50
|
+
const { row, col } = (e.target as HTMLElement).dataset;
|
|
51
|
+
if (!row || !col) return;
|
|
52
|
+
selectDom.dataset.row = row;
|
|
53
|
+
selectDom.dataset.col = col;
|
|
54
|
+
updateSelectBlockItems();
|
|
55
|
+
});
|
|
56
|
+
selectBlock.addEventListener('mouseleave', () => {
|
|
57
|
+
selectDom.removeAttribute('data-row');
|
|
58
|
+
selectDom.removeAttribute('data-col');
|
|
59
|
+
updateSelectBlockItems();
|
|
60
|
+
});
|
|
61
|
+
selectBlock.addEventListener('click', () => {
|
|
62
|
+
const { row, col } = selectDom.dataset;
|
|
63
|
+
if (!row || !col) return;
|
|
64
|
+
options.onSelect && options.onSelect(Number(row), Number(col));
|
|
65
|
+
});
|
|
66
|
+
selectDom.appendChild(selectBlock);
|
|
67
|
+
|
|
68
|
+
if (options.customBtn) {
|
|
69
|
+
const texts = options.texts || {};
|
|
70
|
+
const selectCustom = document.createElement('div');
|
|
71
|
+
selectCustom.classList.add(bem.be('custom'));
|
|
72
|
+
selectCustom.textContent = texts.customBtnText || 'Custom';
|
|
73
|
+
selectCustom.addEventListener('click', async () => {
|
|
74
|
+
const res = await showTableCreator(texts);
|
|
75
|
+
if (res) {
|
|
76
|
+
options.onSelect && options.onSelect(res.row, res.col);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
selectDom.appendChild(selectCustom);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return selectDom;
|
|
83
|
+
};
|