quill-table-up 3.1.2 → 3.2.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/README.md +7 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +168 -146
- package/dist/index.js +47 -47
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +52 -52
- package/dist/index.umd.js.map +1 -1
- package/package.json +22 -24
- package/src/__tests__/e2e/custom-creator.test.ts +44 -44
- package/src/__tests__/e2e/editor-page.ts +77 -77
- package/src/__tests__/e2e/table-align.test.ts +104 -104
- package/src/__tests__/e2e/table-blots.test.ts +169 -169
- package/src/__tests__/e2e/table-caption.test.ts +134 -134
- package/src/__tests__/e2e/table-clipboard.test.ts +20 -20
- package/src/__tests__/e2e/table-hack.test.ts +151 -151
- package/src/__tests__/e2e/table-keyboard-handler.test.ts +12 -3
- package/src/__tests__/e2e/table-menu.test.ts +172 -172
- package/src/__tests__/e2e/table-resize.test.ts +654 -9
- package/src/__tests__/e2e/table-scrollbar.test.ts +144 -144
- package/src/__tests__/e2e/table-selection.test.ts +563 -563
- package/src/__tests__/e2e/types.d.ts +7 -7
- package/src/__tests__/e2e/utils.ts +52 -52
- package/src/__tests__/unit/table-blots.test.ts +720 -720
- package/src/__tests__/unit/table-caption.test.ts +234 -234
- package/src/__tests__/unit/table-cell-merge.test.ts +724 -724
- package/src/__tests__/unit/table-clipboard.test.ts +2176 -2176
- package/src/__tests__/unit/table-hack.test.ts +1014 -1014
- package/src/__tests__/unit/table-insert.test.ts +926 -926
- package/src/__tests__/unit/table-redo-undo.test.ts +2429 -2429
- package/src/__tests__/unit/table-remove.test.ts +343 -343
- package/src/__tests__/unit/utils.test-d.ts +49 -49
- package/src/__tests__/unit/utils.test.ts +711 -711
- package/src/__tests__/unit/utils.ts +307 -307
- package/src/__tests__/unit/vitest.d.ts +14 -14
- package/src/formats/container-format.ts +107 -107
- package/src/formats/overrides/block-embed.ts +72 -72
- package/src/formats/overrides/block.ts +95 -95
- package/src/formats/overrides/index.ts +3 -3
- package/src/formats/overrides/scroll.ts +70 -70
- package/src/formats/table-body-format.ts +52 -52
- package/src/formats/table-caption-format.ts +116 -116
- package/src/formats/table-cell-format.ts +304 -304
- package/src/formats/table-cell-inner-format.ts +403 -398
- package/src/formats/table-colgroup-format.ts +136 -136
- package/src/formats/table-foot-format.ts +7 -7
- package/src/formats/table-head-format.ts +7 -7
- package/src/formats/table-main-format.ts +1 -1
- package/src/formats/table-row-format.ts +218 -210
- package/src/formats/utils.ts +6 -6
- package/src/index.ts +19 -19
- package/src/modules/index.ts +7 -7
- package/src/modules/table-align.ts +131 -131
- package/src/modules/table-clipboard/table-clipboard.ts +6 -8
- package/src/modules/table-dom-selector.ts +33 -33
- package/src/modules/table-menu/constants.ts +223 -223
- package/src/modules/table-menu/index.ts +4 -4
- package/src/modules/table-menu/table-menu-common.ts +330 -329
- package/src/modules/table-menu/table-menu-contextmenu.ts +111 -118
- package/src/modules/table-menu/table-menu-select.ts +96 -94
- package/src/modules/table-resize/index.ts +5 -5
- package/src/modules/table-resize/table-resize-box.ts +714 -363
- package/src/modules/table-resize/table-resize-common.ts +246 -382
- package/src/modules/table-resize/table-resize-drag.ts +241 -0
- package/src/modules/table-resize/table-resize-line.ts +244 -182
- package/src/modules/table-resize/table-resize-scale.ts +174 -173
- package/src/modules/table-resize/utils.ts +84 -3
- package/src/modules/table-scrollbar.ts +292 -292
- package/src/modules/table-selection.ts +613 -669
- package/src/style/button.less +45 -45
- package/src/style/color-picker.less +136 -136
- package/src/style/dialog.less +53 -53
- package/src/style/functions.less +9 -9
- package/src/style/index.less +120 -120
- package/src/style/input.less +64 -64
- package/src/style/select-box.less +52 -52
- package/src/style/table-creator.less +56 -56
- package/src/style/table-menu.less +125 -125
- package/src/style/table-resize-scale.less +31 -31
- package/src/style/table-resize.less +249 -202
- package/src/style/table-scrollbar.less +49 -49
- package/src/style/table-selection.less +23 -23
- package/src/style/tooltip.less +19 -19
- package/src/style/variables.less +1 -1
- package/src/svg/arrow-up-down.svg +11 -11
- package/src/svg/convert-cell.svg +7 -7
- package/src/table-up.ts +1360 -1360
- package/src/types.d.ts +4 -4
- package/src/utils/bem.ts +23 -23
- package/src/utils/blot-helper.ts +101 -105
- package/src/utils/color.ts +109 -109
- package/src/utils/components/button.ts +22 -22
- package/src/utils/components/color-picker.ts +236 -236
- package/src/utils/components/dialog.ts +83 -41
- package/src/utils/components/index.ts +6 -6
- package/src/utils/components/input.ts +74 -74
- package/src/utils/components/table/creator.ts +89 -89
- package/src/utils/components/table/index.ts +2 -2
- package/src/utils/components/table/select-box.ts +78 -78
- package/src/utils/components/tooltip.ts +179 -189
- package/src/utils/constants.ts +125 -124
- package/src/utils/drag-helper.ts +112 -0
- package/src/utils/index.ts +15 -14
- package/src/utils/is.ts +9 -9
- package/src/utils/position.ts +60 -60
- package/src/utils/resize-observer-helper.ts +47 -47
- package/src/utils/scroll.ts +145 -47
- package/src/utils/style-helper.ts +47 -47
- package/src/utils/transformer.ts +10 -10
- package/src/utils/transition-event-helper.ts +8 -8
- package/src/utils/types.ts +156 -157
- package/src/utils/utils.ts +12 -12
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { Position } from './types';
|
|
2
|
+
|
|
3
|
+
export interface DragPosition {
|
|
4
|
+
startPosition: Position;
|
|
5
|
+
position: Position;
|
|
6
|
+
movePosition: Position;
|
|
7
|
+
}
|
|
8
|
+
export interface DragElementOptions {
|
|
9
|
+
axis: 'x' | 'y' | 'both';
|
|
10
|
+
onStart: (position: DragPosition, e: PointerEvent) => void | boolean;
|
|
11
|
+
onMove: (position: DragPosition, e: PointerEvent) => void;
|
|
12
|
+
onEnd: (position: DragPosition, e: PointerEvent) => void;
|
|
13
|
+
buttons: number[];
|
|
14
|
+
container: HTMLElement;
|
|
15
|
+
draggingElement: HTMLElement | Window | Document;
|
|
16
|
+
exact: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function dragElement(target: HTMLElement, options: Partial<DragElementOptions> = {}) {
|
|
20
|
+
const {
|
|
21
|
+
axis = 'both',
|
|
22
|
+
onMove = () => {},
|
|
23
|
+
onStart = () => {},
|
|
24
|
+
onEnd = () => {},
|
|
25
|
+
buttons = [0],
|
|
26
|
+
container,
|
|
27
|
+
draggingElement = document,
|
|
28
|
+
exact = true,
|
|
29
|
+
} = options;
|
|
30
|
+
|
|
31
|
+
let position = { x: 0, y: 0 };
|
|
32
|
+
let startPosition: Position | undefined;
|
|
33
|
+
let startPoint = { x: 0, y: 0 };
|
|
34
|
+
|
|
35
|
+
function updatePositionByEvent(e: PointerEvent) {
|
|
36
|
+
if (!startPosition) return;
|
|
37
|
+
const targetRect = target.getBoundingClientRect();
|
|
38
|
+
let { x, y } = position;
|
|
39
|
+
if (axis === 'x' || axis === 'both') {
|
|
40
|
+
x = e.clientX - startPosition.x;
|
|
41
|
+
if (container) {
|
|
42
|
+
x = Math.min(Math.max(0, x), container.scrollWidth - targetRect!.width);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (axis === 'y' || axis === 'both') {
|
|
46
|
+
y = e.clientY - startPosition.y;
|
|
47
|
+
if (container) {
|
|
48
|
+
y = Math.min(Math.max(0, y), container.scrollHeight - targetRect!.height);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
position = {
|
|
52
|
+
x,
|
|
53
|
+
y,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function handlePointerDown(e: PointerEvent) {
|
|
57
|
+
if (!buttons.includes(e.button)) return;
|
|
58
|
+
if (exact && e.target !== target) return;
|
|
59
|
+
|
|
60
|
+
(draggingElement as HTMLElement).addEventListener('pointerup', handlePointerUp);
|
|
61
|
+
(draggingElement as HTMLElement).addEventListener('pointermove', handlePointerMove);
|
|
62
|
+
|
|
63
|
+
const containerRect = container?.getBoundingClientRect?.();
|
|
64
|
+
const targetRect = target.getBoundingClientRect();
|
|
65
|
+
const pos = {
|
|
66
|
+
x: e.clientX - (container ? targetRect.left - containerRect!.left + container.scrollLeft : targetRect.left),
|
|
67
|
+
y: e.clientY - (container ? targetRect.top - containerRect!.top + container.scrollTop : targetRect.top),
|
|
68
|
+
};
|
|
69
|
+
startPoint = { x: e.clientX, y: e.clientY };
|
|
70
|
+
if (onStart({
|
|
71
|
+
position,
|
|
72
|
+
startPosition: pos,
|
|
73
|
+
movePosition: { x: e.clientX - startPoint.x, y: e.clientY - startPoint.y },
|
|
74
|
+
}, e) === false) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
startPosition = pos;
|
|
79
|
+
position = pos;
|
|
80
|
+
}
|
|
81
|
+
function handlePointerMove(e: PointerEvent) {
|
|
82
|
+
if (!startPosition) return;
|
|
83
|
+
updatePositionByEvent(e);
|
|
84
|
+
onMove({
|
|
85
|
+
position,
|
|
86
|
+
startPosition,
|
|
87
|
+
movePosition: { x: e.clientX - startPoint.x, y: e.clientY - startPoint.y },
|
|
88
|
+
}, e);
|
|
89
|
+
}
|
|
90
|
+
function handlePointerUp(e: PointerEvent) {
|
|
91
|
+
(draggingElement as HTMLElement).removeEventListener('pointermove', handlePointerMove);
|
|
92
|
+
(draggingElement as HTMLElement).removeEventListener('pointerup', handlePointerUp);
|
|
93
|
+
updatePositionByEvent(e);
|
|
94
|
+
onEnd({
|
|
95
|
+
position,
|
|
96
|
+
startPosition: startPosition!,
|
|
97
|
+
movePosition: { x: e.clientX - startPoint.x, y: e.clientY - startPoint.y },
|
|
98
|
+
}, e);
|
|
99
|
+
startPosition = undefined;
|
|
100
|
+
startPoint = { x: 0, y: 0 };
|
|
101
|
+
position = { x: 0, y: 0 };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
(draggingElement as HTMLElement).addEventListener('pointerdown', handlePointerDown);
|
|
105
|
+
|
|
106
|
+
const stop = () => {
|
|
107
|
+
(draggingElement as HTMLElement).removeEventListener('pointerdown', handlePointerDown);
|
|
108
|
+
};
|
|
109
|
+
return {
|
|
110
|
+
stop,
|
|
111
|
+
};
|
|
112
|
+
}
|
package/src/utils/index.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
export * from './bem';
|
|
2
|
-
export * from './blot-helper';
|
|
3
|
-
export * from './color';
|
|
4
|
-
export * from './components';
|
|
5
|
-
export * from './constants';
|
|
6
|
-
export * from './
|
|
7
|
-
export * from './
|
|
8
|
-
export * from './
|
|
9
|
-
export * from './
|
|
10
|
-
export * from './
|
|
11
|
-
export * from './
|
|
12
|
-
export * from './
|
|
13
|
-
export * from './
|
|
14
|
-
export * from './
|
|
1
|
+
export * from './bem';
|
|
2
|
+
export * from './blot-helper';
|
|
3
|
+
export * from './color';
|
|
4
|
+
export * from './components';
|
|
5
|
+
export * from './constants';
|
|
6
|
+
export * from './drag-helper';
|
|
7
|
+
export * from './is';
|
|
8
|
+
export * from './position';
|
|
9
|
+
export * from './resize-observer-helper';
|
|
10
|
+
export * from './scroll';
|
|
11
|
+
export * from './style-helper';
|
|
12
|
+
export * from './transformer';
|
|
13
|
+
export * from './transition-event-helper';
|
|
14
|
+
export * from './types';
|
|
15
|
+
export * from './utils';
|
package/src/utils/is.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export const isFunction = (val: unknown): val is Function => typeof val === 'function';
|
|
2
|
-
export const isBoolean = (val: unknown): val is boolean => typeof val === 'boolean';
|
|
3
|
-
export const isArray = Array.isArray;
|
|
4
|
-
export const isString = (val: unknown): val is string => typeof val === 'string';
|
|
5
|
-
export const isNumber = (val: unknown): val is number => typeof val === 'number';
|
|
6
|
-
export const isObject = (val: unknown): val is Record<any, any> => val !== null && typeof val === 'object';
|
|
7
|
-
export const isUndefined = (val: unknown): val is undefined => val === undefined;
|
|
8
|
-
export const isValidCellspan = (val: unknown): boolean => !Number.isNaN(val) && Number(val) > 0;
|
|
9
|
-
export const ensureArray = <T>(val: T | T[]): T[] => isArray(val) ? val : [val];
|
|
1
|
+
export const isFunction = (val: unknown): val is Function => typeof val === 'function';
|
|
2
|
+
export const isBoolean = (val: unknown): val is boolean => typeof val === 'boolean';
|
|
3
|
+
export const isArray = Array.isArray;
|
|
4
|
+
export const isString = (val: unknown): val is string => typeof val === 'string';
|
|
5
|
+
export const isNumber = (val: unknown): val is number => typeof val === 'number';
|
|
6
|
+
export const isObject = (val: unknown): val is Record<any, any> => val !== null && typeof val === 'object';
|
|
7
|
+
export const isUndefined = (val: unknown): val is undefined => val === undefined;
|
|
8
|
+
export const isValidCellspan = (val: unknown): boolean => !Number.isNaN(val) && Number(val) > 0;
|
|
9
|
+
export const ensureArray = <T>(val: T | T[]): T[] => isArray(val) ? val : [val];
|
package/src/utils/position.ts
CHANGED
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
import type { RelactiveRect } from './types';
|
|
2
|
-
|
|
3
|
-
export function isRectanglesIntersect<T extends Omit<RelactiveRect, 'width' | 'height'>>(a: T, b: T, tolerance = 0, edgeJudge: boolean = true) {
|
|
4
|
-
const { x: minAx, y: minAy, x1: maxAx, y1: maxAy } = a;
|
|
5
|
-
const { x: minBx, y: minBy, x1: maxBx, y1: maxBy } = b;
|
|
6
|
-
|
|
7
|
-
let notOverlapX;
|
|
8
|
-
let notOverlapY;
|
|
9
|
-
if (edgeJudge) {
|
|
10
|
-
notOverlapX = maxAx < minBx + tolerance || minAx - tolerance > maxBx;
|
|
11
|
-
notOverlapY = maxAy < minBy + tolerance || minAy - tolerance > maxBy;
|
|
12
|
-
}
|
|
13
|
-
else {
|
|
14
|
-
notOverlapX = maxAx <= minBx + tolerance || minAx - tolerance >= maxBx;
|
|
15
|
-
notOverlapY = maxAy <= minBy + tolerance || minAy - tolerance >= maxBy;
|
|
16
|
-
}
|
|
17
|
-
return !(notOverlapX || notOverlapY);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function getRelativeRect(targetRect: Omit<RelactiveRect, 'x1' | 'y1'>, container: HTMLElement): RelactiveRect {
|
|
21
|
-
const containerRect = container.getBoundingClientRect();
|
|
22
|
-
return {
|
|
23
|
-
x: targetRect.x - containerRect.x - container.scrollLeft,
|
|
24
|
-
y: targetRect.y - containerRect.y - container.scrollTop,
|
|
25
|
-
x1: targetRect.x - containerRect.x - container.scrollLeft + targetRect.width,
|
|
26
|
-
y1: targetRect.y - containerRect.y - container.scrollTop + targetRect.height,
|
|
27
|
-
width: targetRect.width,
|
|
28
|
-
height: targetRect.height,
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const viewportPadding = 8;
|
|
33
|
-
export function limitDomInViewPort(rect: { left: number; top: number; width: number; height: number }) {
|
|
34
|
-
let { left, top, width, height } = rect;
|
|
35
|
-
const { clientWidth, clientHeight } = document.documentElement;
|
|
36
|
-
let leftLimited = false;
|
|
37
|
-
let topLimited = false;
|
|
38
|
-
if (left + width > clientWidth) {
|
|
39
|
-
left = clientWidth - width - viewportPadding;
|
|
40
|
-
leftLimited = true;
|
|
41
|
-
}
|
|
42
|
-
else if (left < 0) {
|
|
43
|
-
left = viewportPadding;
|
|
44
|
-
leftLimited = true;
|
|
45
|
-
}
|
|
46
|
-
if (top + height > clientHeight) {
|
|
47
|
-
top = clientHeight - height - viewportPadding;
|
|
48
|
-
topLimited = true;
|
|
49
|
-
}
|
|
50
|
-
else if (top < 0) {
|
|
51
|
-
top = viewportPadding;
|
|
52
|
-
topLimited = true;
|
|
53
|
-
}
|
|
54
|
-
return {
|
|
55
|
-
left,
|
|
56
|
-
top,
|
|
57
|
-
leftLimited,
|
|
58
|
-
topLimited,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
1
|
+
import type { RelactiveRect } from './types';
|
|
2
|
+
|
|
3
|
+
export function isRectanglesIntersect<T extends Omit<RelactiveRect, 'width' | 'height'>>(a: T, b: T, tolerance = 0, edgeJudge: boolean = true) {
|
|
4
|
+
const { x: minAx, y: minAy, x1: maxAx, y1: maxAy } = a;
|
|
5
|
+
const { x: minBx, y: minBy, x1: maxBx, y1: maxBy } = b;
|
|
6
|
+
|
|
7
|
+
let notOverlapX;
|
|
8
|
+
let notOverlapY;
|
|
9
|
+
if (edgeJudge) {
|
|
10
|
+
notOverlapX = maxAx < minBx + tolerance || minAx - tolerance > maxBx;
|
|
11
|
+
notOverlapY = maxAy < minBy + tolerance || minAy - tolerance > maxBy;
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
notOverlapX = maxAx <= minBx + tolerance || minAx - tolerance >= maxBx;
|
|
15
|
+
notOverlapY = maxAy <= minBy + tolerance || minAy - tolerance >= maxBy;
|
|
16
|
+
}
|
|
17
|
+
return !(notOverlapX || notOverlapY);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getRelativeRect(targetRect: Omit<RelactiveRect, 'x1' | 'y1'>, container: HTMLElement): RelactiveRect {
|
|
21
|
+
const containerRect = container.getBoundingClientRect();
|
|
22
|
+
return {
|
|
23
|
+
x: targetRect.x - containerRect.x - container.scrollLeft,
|
|
24
|
+
y: targetRect.y - containerRect.y - container.scrollTop,
|
|
25
|
+
x1: targetRect.x - containerRect.x - container.scrollLeft + targetRect.width,
|
|
26
|
+
y1: targetRect.y - containerRect.y - container.scrollTop + targetRect.height,
|
|
27
|
+
width: targetRect.width,
|
|
28
|
+
height: targetRect.height,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const viewportPadding = 8;
|
|
33
|
+
export function limitDomInViewPort(rect: { left: number; top: number; width: number; height: number }) {
|
|
34
|
+
let { left, top, width, height } = rect;
|
|
35
|
+
const { clientWidth, clientHeight } = document.documentElement;
|
|
36
|
+
let leftLimited = false;
|
|
37
|
+
let topLimited = false;
|
|
38
|
+
if (left + width > clientWidth) {
|
|
39
|
+
left = clientWidth - width - viewportPadding;
|
|
40
|
+
leftLimited = true;
|
|
41
|
+
}
|
|
42
|
+
else if (left < 0) {
|
|
43
|
+
left = viewportPadding;
|
|
44
|
+
leftLimited = true;
|
|
45
|
+
}
|
|
46
|
+
if (top + height > clientHeight) {
|
|
47
|
+
top = clientHeight - height - viewportPadding;
|
|
48
|
+
topLimited = true;
|
|
49
|
+
}
|
|
50
|
+
else if (top < 0) {
|
|
51
|
+
top = viewportPadding;
|
|
52
|
+
topLimited = true;
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
left,
|
|
56
|
+
top,
|
|
57
|
+
leftLimited,
|
|
58
|
+
topLimited,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
export interface CreateResizeObserverOptions {
|
|
2
|
-
ignoreFirstBind: boolean;
|
|
3
|
-
}
|
|
4
|
-
export function createResizeObserver(callback: (entries: ResizeObserverEntry[]) => void, options: Partial<CreateResizeObserverOptions> = {}) {
|
|
5
|
-
const ignoreFirstBindSymbol = Symbol('ignoreFirstBind');
|
|
6
|
-
type ResizeObserveTarget = Element & { [ignoreFirstBindSymbol]?: boolean };
|
|
7
|
-
const observeEls: Set<ResizeObserveTarget> = new Set();
|
|
8
|
-
const observer = new ResizeObserver((entries) => {
|
|
9
|
-
// prevent when element first bind
|
|
10
|
-
if (
|
|
11
|
-
options.ignoreFirstBind
|
|
12
|
-
&& entries.some((entry) => {
|
|
13
|
-
const target = entry.target as ResizeObserveTarget;
|
|
14
|
-
const originVal = target[ignoreFirstBindSymbol];
|
|
15
|
-
target[ignoreFirstBindSymbol] = true;
|
|
16
|
-
return !originVal;
|
|
17
|
-
})
|
|
18
|
-
) {
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
callback(entries);
|
|
22
|
-
});
|
|
23
|
-
const originObserve = observer.observe;
|
|
24
|
-
observer.observe = (target: ResizeObserveTarget, options?: ResizeObserverOptions) => {
|
|
25
|
-
observeEls.add(target);
|
|
26
|
-
originObserve
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const originUnobserve = observer.unobserve;
|
|
30
|
-
observer.unobserve = (target: ResizeObserveTarget) => {
|
|
31
|
-
if (observeEls.has(target)) {
|
|
32
|
-
observeEls.delete(target);
|
|
33
|
-
target[ignoreFirstBindSymbol] = undefined;
|
|
34
|
-
}
|
|
35
|
-
originUnobserve.call(observer, target);
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const originDisconnect = observer.disconnect;
|
|
39
|
-
observer.disconnect = () => {
|
|
40
|
-
for (const target of observeEls.values()) {
|
|
41
|
-
target[ignoreFirstBindSymbol] = undefined;
|
|
42
|
-
}
|
|
43
|
-
originDisconnect.call(observer);
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
return observer;
|
|
47
|
-
}
|
|
1
|
+
export interface CreateResizeObserverOptions {
|
|
2
|
+
ignoreFirstBind: boolean;
|
|
3
|
+
}
|
|
4
|
+
export function createResizeObserver(callback: (entries: ResizeObserverEntry[]) => void, options: Partial<CreateResizeObserverOptions> = {}) {
|
|
5
|
+
const ignoreFirstBindSymbol = Symbol('ignoreFirstBind');
|
|
6
|
+
type ResizeObserveTarget = Element & { [ignoreFirstBindSymbol]?: boolean };
|
|
7
|
+
const observeEls: Set<ResizeObserveTarget> = new Set();
|
|
8
|
+
const observer = new ResizeObserver((entries) => {
|
|
9
|
+
// prevent when element first bind
|
|
10
|
+
if (
|
|
11
|
+
options.ignoreFirstBind
|
|
12
|
+
&& entries.some((entry) => {
|
|
13
|
+
const target = entry.target as ResizeObserveTarget;
|
|
14
|
+
const originVal = target[ignoreFirstBindSymbol];
|
|
15
|
+
target[ignoreFirstBindSymbol] = true;
|
|
16
|
+
return !originVal;
|
|
17
|
+
})
|
|
18
|
+
) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
callback(entries);
|
|
22
|
+
});
|
|
23
|
+
const originObserve = observer.observe.bind(observer);
|
|
24
|
+
observer.observe = (target: ResizeObserveTarget, options?: ResizeObserverOptions) => {
|
|
25
|
+
observeEls.add(target);
|
|
26
|
+
originObserve(target, options);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const originUnobserve = observer.unobserve;
|
|
30
|
+
observer.unobserve = (target: ResizeObserveTarget) => {
|
|
31
|
+
if (observeEls.has(target)) {
|
|
32
|
+
observeEls.delete(target);
|
|
33
|
+
target[ignoreFirstBindSymbol] = undefined;
|
|
34
|
+
}
|
|
35
|
+
originUnobserve.call(observer, target);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const originDisconnect = observer.disconnect;
|
|
39
|
+
observer.disconnect = () => {
|
|
40
|
+
for (const target of observeEls.values()) {
|
|
41
|
+
target[ignoreFirstBindSymbol] = undefined;
|
|
42
|
+
}
|
|
43
|
+
originDisconnect.call(observer);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
return observer;
|
|
47
|
+
}
|
package/src/utils/scroll.ts
CHANGED
|
@@ -1,47 +1,145 @@
|
|
|
1
|
-
export interface ScrollHandle {
|
|
2
|
-
scrollHandler: [HTMLElement, (e: Event) => void][];
|
|
3
|
-
}
|
|
4
|
-
export function addScrollEvent(this: ScrollHandle, dom: HTMLElement, handle: (e: Event) => void) {
|
|
5
|
-
dom.addEventListener('scroll', handle);
|
|
6
|
-
this.scrollHandler.push([dom, handle]);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function
|
|
10
|
-
for (let i = 0; i < this.scrollHandler.length; i++) {
|
|
11
|
-
const [
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
1
|
+
export interface ScrollHandle {
|
|
2
|
+
scrollHandler: [HTMLElement, (e: Event) => void][];
|
|
3
|
+
}
|
|
4
|
+
export function addScrollEvent(this: ScrollHandle, dom: HTMLElement, handle: (e: Event) => void) {
|
|
5
|
+
dom.addEventListener('scroll', handle);
|
|
6
|
+
this.scrollHandler.push([dom, handle]);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function removeScrollEvent(this: ScrollHandle, dom: HTMLElement, handle: (e: Event) => void) {
|
|
10
|
+
for (let i = 0; i < this.scrollHandler.length; i++) {
|
|
11
|
+
const [_dom, _handle] = this.scrollHandler[i];
|
|
12
|
+
if (_dom === dom && _handle === handle) {
|
|
13
|
+
this.scrollHandler.splice(i, 1);
|
|
14
|
+
break;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function clearScrollEvent(this: ScrollHandle) {
|
|
20
|
+
for (const [dom, handle] of this.scrollHandler) {
|
|
21
|
+
dom.removeEventListener('scroll', handle);
|
|
22
|
+
}
|
|
23
|
+
this.scrollHandler = [];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function getElementScrollPosition(el: HTMLElement) {
|
|
27
|
+
return {
|
|
28
|
+
y: el.scrollTop,
|
|
29
|
+
x: el.scrollLeft,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function getScrollBarWidth({ target = document.body } = {}): number {
|
|
34
|
+
const outer = document.createElement('div');
|
|
35
|
+
Object.assign(outer.style, {
|
|
36
|
+
visibility: 'hidden',
|
|
37
|
+
width: '100px',
|
|
38
|
+
height: '100%',
|
|
39
|
+
overflow: 'auto',
|
|
40
|
+
position: 'absolute',
|
|
41
|
+
top: '-9999px',
|
|
42
|
+
});
|
|
43
|
+
target.appendChild(outer);
|
|
44
|
+
|
|
45
|
+
const widthNoScroll = outer.offsetWidth;
|
|
46
|
+
outer.style.overflow = 'scroll';
|
|
47
|
+
|
|
48
|
+
const inner = document.createElement('div');
|
|
49
|
+
inner.style.width = '100%';
|
|
50
|
+
outer.appendChild(inner);
|
|
51
|
+
|
|
52
|
+
const widthWithScroll = inner.offsetWidth;
|
|
53
|
+
outer.parentNode?.removeChild(outer);
|
|
54
|
+
|
|
55
|
+
return widthNoScroll - widthWithScroll;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export class AutoScroller {
|
|
59
|
+
mouseY = 0;
|
|
60
|
+
mouseX = 0;
|
|
61
|
+
private animationId: number | null = null;
|
|
62
|
+
|
|
63
|
+
constructor(
|
|
64
|
+
public scrollThresholdX: number = 50,
|
|
65
|
+
public scrollThresholdY: number = 20,
|
|
66
|
+
public maxScrollSpeed: number = 20,
|
|
67
|
+
) {}
|
|
68
|
+
|
|
69
|
+
checkMinY(containerRect: DOMRect) {
|
|
70
|
+
return this.mouseY < containerRect.top + this.scrollThresholdY;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
checkMaxY(containerRect: DOMRect) {
|
|
74
|
+
return this.mouseY > containerRect.bottom - this.scrollThresholdY;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
checkMinX(containerRect: DOMRect) {
|
|
78
|
+
return this.mouseX < containerRect.left + this.scrollThresholdX;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
checkMaxX(containerRect: DOMRect) {
|
|
82
|
+
return this.mouseX > containerRect.right - this.scrollThresholdX;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
start(container: HTMLElement, onScroll?: (speedX: number, speedY: number) => void) {
|
|
86
|
+
// before call `start` also need call `updateMousePosition`
|
|
87
|
+
// consider if needed put `getBoundingClientRect` in `scroll`
|
|
88
|
+
const rect = container.getBoundingClientRect();
|
|
89
|
+
const scroll = () => {
|
|
90
|
+
let scrolled = false;
|
|
91
|
+
let speedX = 0;
|
|
92
|
+
let speedY = 0;
|
|
93
|
+
if (this.checkMinY(rect)) {
|
|
94
|
+
const distance = rect.top + this.scrollThresholdY - this.mouseY;
|
|
95
|
+
const speed = Math.min(distance / this.scrollThresholdY * this.maxScrollSpeed, this.maxScrollSpeed);
|
|
96
|
+
container.scrollTop -= speed;
|
|
97
|
+
speedY = -1 * speed;
|
|
98
|
+
scrolled = true;
|
|
99
|
+
}
|
|
100
|
+
else if (this.checkMaxY(rect)) {
|
|
101
|
+
const distance = this.mouseY - (rect.bottom - this.scrollThresholdY);
|
|
102
|
+
const speed = Math.min(distance / this.scrollThresholdY * this.maxScrollSpeed, this.maxScrollSpeed);
|
|
103
|
+
container.scrollTop += speed;
|
|
104
|
+
speedY = speed;
|
|
105
|
+
scrolled = true;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (this.checkMinX(rect)) {
|
|
109
|
+
const distance = rect.left + this.scrollThresholdX - this.mouseX;
|
|
110
|
+
const speed = Math.min(distance / this.scrollThresholdX * this.maxScrollSpeed, this.maxScrollSpeed);
|
|
111
|
+
container.scrollLeft -= speed;
|
|
112
|
+
speedX = -1 * speed;
|
|
113
|
+
scrolled = true;
|
|
114
|
+
}
|
|
115
|
+
else if (this.checkMaxX(rect)) {
|
|
116
|
+
const distance = this.mouseX - (rect.right - this.scrollThresholdX);
|
|
117
|
+
const speed = Math.min(distance / this.scrollThresholdX * this.maxScrollSpeed, this.maxScrollSpeed);
|
|
118
|
+
container.scrollLeft += speed;
|
|
119
|
+
speedX = speed;
|
|
120
|
+
scrolled = true;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (scrolled && onScroll) {
|
|
124
|
+
onScroll(speedX, speedY);
|
|
125
|
+
}
|
|
126
|
+
this.animationId = requestAnimationFrame(scroll);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
this.animationId = requestAnimationFrame(scroll);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// update mouse position when `mousemove` event triggered
|
|
133
|
+
updateMousePosition(x: number, y: number) {
|
|
134
|
+
this.mouseX = x;
|
|
135
|
+
this.mouseY = y;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// stop listening scroll event when `mouseup` event triggered
|
|
139
|
+
stop() {
|
|
140
|
+
if (this.animationId !== null) {
|
|
141
|
+
cancelAnimationFrame(this.animationId);
|
|
142
|
+
this.animationId = null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
import { toCamelCase, toKebabCase } from './transformer';
|
|
2
|
-
|
|
3
|
-
export function getInlineStyles(domNode: HTMLElement): Record<string, string> {
|
|
4
|
-
const inlineStyles: Record<string, string> = {};
|
|
5
|
-
if (!domNode.style.cssText) {
|
|
6
|
-
return inlineStyles;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const cssText = domNode.style.cssText;
|
|
10
|
-
const declarations = cssText.split(';').filter(decl => decl.trim());
|
|
11
|
-
for (const declaration of declarations) {
|
|
12
|
-
const colonIndex = declaration.indexOf(':');
|
|
13
|
-
if (colonIndex === -1) continue;
|
|
14
|
-
|
|
15
|
-
const property = declaration.slice(0, colonIndex).trim();
|
|
16
|
-
const value = declaration.slice(colonIndex + 1).trim();
|
|
17
|
-
inlineStyles[property] = value;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return inlineStyles;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function cssTextToObject(cssText: string): Record<string, string> {
|
|
24
|
-
const styleObject: Record<string, string> = {};
|
|
25
|
-
|
|
26
|
-
const styles = cssText.trim().split(';');
|
|
27
|
-
|
|
28
|
-
for (
|
|
29
|
-
const style =
|
|
30
|
-
if (!style) continue;
|
|
31
|
-
|
|
32
|
-
const colonPosition = style.indexOf(':');
|
|
33
|
-
if (colonPosition === -1) continue;
|
|
34
|
-
|
|
35
|
-
const property = style.slice(0, Math.max(0, colonPosition)).trim();
|
|
36
|
-
const value = style.slice(Math.max(0, colonPosition + 1)).trim();
|
|
37
|
-
styleObject[toCamelCase(property)] = value;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return styleObject;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function objectToCssText(obj: Record<string, any>): string {
|
|
44
|
-
return Object.entries(obj)
|
|
45
|
-
.map(([key, value]) => `${toKebabCase(key)}: ${value};`)
|
|
46
|
-
.join(' ');
|
|
47
|
-
}
|
|
1
|
+
import { toCamelCase, toKebabCase } from './transformer';
|
|
2
|
+
|
|
3
|
+
export function getInlineStyles(domNode: HTMLElement): Record<string, string> {
|
|
4
|
+
const inlineStyles: Record<string, string> = {};
|
|
5
|
+
if (!domNode.style.cssText) {
|
|
6
|
+
return inlineStyles;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const cssText = domNode.style.cssText;
|
|
10
|
+
const declarations = cssText.split(';').filter(decl => decl.trim());
|
|
11
|
+
for (const declaration of declarations) {
|
|
12
|
+
const colonIndex = declaration.indexOf(':');
|
|
13
|
+
if (colonIndex === -1) continue;
|
|
14
|
+
|
|
15
|
+
const property = declaration.slice(0, colonIndex).trim();
|
|
16
|
+
const value = declaration.slice(colonIndex + 1).trim();
|
|
17
|
+
inlineStyles[property] = value;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return inlineStyles;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function cssTextToObject(cssText: string): Record<string, string> {
|
|
24
|
+
const styleObject: Record<string, string> = {};
|
|
25
|
+
|
|
26
|
+
const styles = cssText.trim().split(';');
|
|
27
|
+
|
|
28
|
+
for (const item of styles) {
|
|
29
|
+
const style = item.trim();
|
|
30
|
+
if (!style) continue;
|
|
31
|
+
|
|
32
|
+
const colonPosition = style.indexOf(':');
|
|
33
|
+
if (colonPosition === -1) continue;
|
|
34
|
+
|
|
35
|
+
const property = style.slice(0, Math.max(0, colonPosition)).trim();
|
|
36
|
+
const value = style.slice(Math.max(0, colonPosition + 1)).trim();
|
|
37
|
+
styleObject[toCamelCase(property)] = value;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return styleObject;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function objectToCssText(obj: Record<string, any>): string {
|
|
44
|
+
return Object.entries(obj)
|
|
45
|
+
.map(([key, value]) => `${toKebabCase(key)}: ${value};`)
|
|
46
|
+
.join(' ');
|
|
47
|
+
}
|