wenay-react2 1.0.20 → 1.0.22
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 +383 -382
- package/lib/common/api.d.ts +28 -0
- package/lib/common/api.js +41 -16
- package/lib/common/src/components/Buttons/MiniButton.d.ts +1 -0
- package/lib/common/src/components/Buttons/MiniButton.js +2 -2
- package/lib/common/src/components/Dnd/DraggableOutlineDiv.d.ts +1 -0
- package/lib/common/src/components/Dnd/DraggableOutlineDiv.js +7 -6
- package/lib/common/src/components/Dnd/RNDFunc.js +28 -17
- package/lib/common/src/components/Dnd/RNDFunc3.d.ts +27 -17
- package/lib/common/src/components/Dnd/RNDFunc3.js +107 -105
- package/lib/common/src/components/Dnd/Resizable.js +3 -3
- package/lib/common/src/components/Menu/RightMenu.js +11 -17
- package/lib/common/src/components/Menu/StickerMenu.js +42 -28
- package/lib/common/src/components/Menu/index.d.ts +1 -1
- package/lib/common/src/components/Menu/index.js +1 -1
- package/lib/common/src/components/Modal/LeftModal.d.ts +14 -12
- package/lib/common/src/components/Modal/LeftModal.js +30 -16
- package/lib/common/src/components/Modal/Modal.d.ts +26 -12
- package/lib/common/src/components/Modal/Modal.js +66 -89
- package/lib/common/src/components/Modal/ModalContextProvider.d.ts +20 -3
- package/lib/common/src/components/Modal/ModalContextProvider.js +35 -8
- package/lib/common/src/components/MyResizeObserver.js +15 -18
- package/lib/common/src/components/Other.js +3 -2
- package/lib/common/src/components/Parameters.js +7 -6
- package/lib/common/src/components/ParametersEngine.js +25 -54
- package/lib/common/src/grid/agGrid4/agGrid4.d.ts +53 -0
- package/lib/common/src/grid/agGrid4/agGrid4.js +132 -0
- package/lib/common/src/grid/agGrid4/core.d.ts +56 -0
- package/lib/common/src/grid/agGrid4/core.js +127 -0
- package/lib/common/src/grid/agGrid4/index.d.ts +6 -0
- package/lib/common/src/grid/agGrid4/index.js +3 -0
- package/lib/common/src/grid/agGrid4/theme.d.ts +7 -0
- package/lib/common/src/grid/agGrid4/theme.js +18 -0
- package/lib/common/src/hooks/useAddDownAnyKey.d.ts +21 -2
- package/lib/common/src/hooks/useAddDownAnyKey.js +46 -8
- package/lib/common/src/hooks/useDraggable.d.ts +20 -2
- package/lib/common/src/hooks/useDraggable.js +146 -115
- package/lib/common/src/hooks/useOutside.d.ts +24 -4
- package/lib/common/src/hooks/useOutside.js +76 -27
- package/lib/common/src/logs/logs.d.ts +5 -2
- package/lib/common/src/logs/logs.js +15 -31
- package/lib/common/src/logs/logs3.d.ts +9 -9
- package/lib/common/src/logs/logs3.js +47 -38
- package/lib/common/src/logs/miniLogs.d.ts +3 -3
- package/lib/common/src/logs/miniLogs.js +28 -36
- package/lib/common/src/menu/menu.d.ts +18 -18
- package/lib/common/src/menu/menu.js +48 -24
- package/lib/common/src/menu/menuMouse.js +4 -1
- package/lib/common/src/menu/menuR.d.ts +2 -2
- package/lib/common/src/menu/menuR.js +34 -34
- package/lib/common/src/myChart/1/myChart.d.ts +5 -5
- package/lib/common/src/myChart/1/myChart.js +58 -47
- package/lib/common/src/myChart/1/myChartTest.js +8 -4
- package/lib/common/src/myChart/chartEngine/chartEngineReact.d.ts +24 -27
- package/lib/common/src/myChart/chartEngine/chartEngineReact.js +166 -117
- package/lib/common/src/styles/styleGrid.d.ts +2 -1
- package/lib/common/src/styles/styleGrid.js +10 -12
- package/lib/common/src/styles/tokens.d.ts +39 -0
- package/lib/common/src/styles/tokens.js +40 -0
- package/lib/common/src/utils/applyTransactionAsyncUpdate.d.ts +7 -0
- package/lib/common/src/utils/applyTransactionAsyncUpdate.js +19 -12
- package/lib/common/src/utils/arrayPromise.d.ts +6 -0
- package/lib/common/src/utils/arrayPromise.js +7 -1
- package/lib/common/src/utils/cache.js +24 -21
- package/lib/common/src/utils/inputAutoStep.d.ts +1 -1
- package/lib/common/src/utils/inputAutoStep.js +30 -19
- package/lib/common/src/utils/mapMemory.js +9 -10
- package/lib/common/updateBy.d.ts +21 -2
- package/lib/common/updateBy.js +66 -31
- package/lib/index.js +0 -2
- package/lib/style/menuRight.css +13 -26
- package/lib/style/style.css +51 -39
- package/lib/style/tokens.css +34 -0
- package/package.json +2 -2
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// Layer 1 (core): pure buffer logic, implemented as a closure factory. No React, no ag-grid.
|
|
2
|
+
// Buffer = the last known row state by id (a memory point). All race handling is here.
|
|
3
|
+
//
|
|
4
|
+
// Differences from agGrid2/3 (optimizations):
|
|
5
|
+
// - dependencies (getId, buffer, api) are captured by the factory once: one entry point,
|
|
6
|
+
// updateData, instead of duplicating buffer/grid branching in the hook;
|
|
7
|
+
// - in-place row merge (Object.assign), with no new object allocated for each update
|
|
8
|
+
// (important on streams: less garbage for GC);
|
|
9
|
+
// - add vs update is decided by a Set of delivered ids, without api.getRowNode per row.
|
|
10
|
+
/**
|
|
11
|
+
* Table core: buffer + delivery to the grid. Lifecycle:
|
|
12
|
+
* 1. updateData at any time: the buffer is always updated, the grid only if attached;
|
|
13
|
+
* 2. attach(api) on onGridReady: sync catches up with everything that arrived earlier;
|
|
14
|
+
* 3. detach() on grid destroy: the core accumulates in the buffer again until the next attach.
|
|
15
|
+
*/
|
|
16
|
+
export function createGridBuffer(deps) {
|
|
17
|
+
const { getId } = deps;
|
|
18
|
+
const buf = deps.externalBuffer ?? {};
|
|
19
|
+
const inGrid = new Set(); // ids already delivered to the grid
|
|
20
|
+
let api = null;
|
|
21
|
+
// --- Buffer ----------------------------------------------------------------
|
|
22
|
+
function upsert(rows) {
|
|
23
|
+
for (const row of rows) {
|
|
24
|
+
const id = getId(row);
|
|
25
|
+
buf[id] = Object.assign(buf[id] ?? {}, row);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// --- Grid delivery ---------------------------------------------------------
|
|
29
|
+
/** Main entry point: push data any time; the buffer decides whether it reaches the grid. */
|
|
30
|
+
function updateData(args) {
|
|
31
|
+
const { newData, removeData, onlyMemo, option } = args;
|
|
32
|
+
if (newData)
|
|
33
|
+
upsert(newData);
|
|
34
|
+
if (removeData)
|
|
35
|
+
for (const row of removeData)
|
|
36
|
+
delete buf[getId(row)];
|
|
37
|
+
if (onlyMemo || !api)
|
|
38
|
+
return;
|
|
39
|
+
const opt = { add: true, update: true, sync: false, ...option };
|
|
40
|
+
const toAdd = [];
|
|
41
|
+
const toUpdate = [];
|
|
42
|
+
for (const row of newData ?? []) {
|
|
43
|
+
const id = getId(row);
|
|
44
|
+
const merged = buf[id];
|
|
45
|
+
if (inGrid.has(id))
|
|
46
|
+
toUpdate.push(merged);
|
|
47
|
+
else
|
|
48
|
+
toAdd.push(merged);
|
|
49
|
+
}
|
|
50
|
+
const add = opt.add ? toAdd : [];
|
|
51
|
+
const update = opt.update ? toUpdate : [];
|
|
52
|
+
// Mark as delivered only the rows that are actually sent to the grid.
|
|
53
|
+
for (const row of add)
|
|
54
|
+
inGrid.add(getId(row));
|
|
55
|
+
// ag-grid finds rows for remove through getRowId, so partial data is enough.
|
|
56
|
+
const remove = removeData?.filter(row => inGrid.delete(getId(row)));
|
|
57
|
+
if (opt.sync) {
|
|
58
|
+
api.applyTransaction({ add, update, remove });
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (add.length || remove?.length)
|
|
62
|
+
api.applyTransaction({ add, remove });
|
|
63
|
+
// Let the grid itself batch updates (asyncTransactionWaitMillis).
|
|
64
|
+
if (update.length)
|
|
65
|
+
api.applyTransactionAsync({ update });
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Bring the whole grid in line with the buffer (the buffer is the source of truth).
|
|
69
|
+
* Missing from the buffer -> remove; missing from the grid -> add; everything else -> update.
|
|
70
|
+
*/
|
|
71
|
+
function sync() {
|
|
72
|
+
if (!api)
|
|
73
|
+
return;
|
|
74
|
+
const add = [];
|
|
75
|
+
const update = [];
|
|
76
|
+
const remove = [];
|
|
77
|
+
const gridIds = new Set();
|
|
78
|
+
api.forEachNode(function classify(node) {
|
|
79
|
+
if (!node.data)
|
|
80
|
+
return;
|
|
81
|
+
const id = getId(node.data);
|
|
82
|
+
gridIds.add(id);
|
|
83
|
+
if (buf[id])
|
|
84
|
+
update.push(buf[id]);
|
|
85
|
+
else
|
|
86
|
+
remove.push(node.data);
|
|
87
|
+
});
|
|
88
|
+
inGrid.clear();
|
|
89
|
+
for (const id in buf) {
|
|
90
|
+
inGrid.add(id);
|
|
91
|
+
if (!gridIds.has(id))
|
|
92
|
+
add.push(buf[id]);
|
|
93
|
+
}
|
|
94
|
+
if (add.length || update.length || remove.length)
|
|
95
|
+
api.applyTransaction({ add, update, remove });
|
|
96
|
+
}
|
|
97
|
+
// --- Lifecycle -------------------------------------------------------------
|
|
98
|
+
function attach(gridApi) {
|
|
99
|
+
api = gridApi;
|
|
100
|
+
sync(); // catch up with the race: data may have arrived before onGridReady
|
|
101
|
+
}
|
|
102
|
+
function detach() {
|
|
103
|
+
api = null;
|
|
104
|
+
inGrid.clear();
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
// for the React binding (or another grid owner)
|
|
108
|
+
control: { attach, detach },
|
|
109
|
+
// public consumer api
|
|
110
|
+
api: { updateData, sync, getId, buffer: buf },
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/** Column comparator: numbers as numbers, empty/NaN values last (respecting inversion). */
|
|
114
|
+
export function numericComparator(map) {
|
|
115
|
+
return (a1, b1, _nodeA, _nodeB, inv) => {
|
|
116
|
+
const [a, b] = map ? map(a1, b1) : [a1, b1];
|
|
117
|
+
const aNum = typeof a == 'number' && !Number.isNaN(a);
|
|
118
|
+
const bNum = typeof b == 'number' && !Number.isNaN(b);
|
|
119
|
+
if (aNum && bNum)
|
|
120
|
+
return a - b;
|
|
121
|
+
if (a == b)
|
|
122
|
+
return 0;
|
|
123
|
+
if (!Number.isNaN(b) && b != null)
|
|
124
|
+
return inv ? -1 : 1;
|
|
125
|
+
return inv ? 1 : -1;
|
|
126
|
+
};
|
|
127
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { createGridBuffer, numericComparator } from './core';
|
|
2
|
+
export type { BufferTable, GetId, GridApiLike, GridBufferCore, GridTransaction, NumberPair, PushOptions, RowId, UpdateArgs, } from './core';
|
|
3
|
+
export { useAgGrid, AgGridMy } from './agGrid4';
|
|
4
|
+
export type { AgGridController, AgGridMyProps, UseAgGridOptions } from './agGrid4';
|
|
5
|
+
export { useAgGridTheme, buildAgTheme } from './theme';
|
|
6
|
+
export type { tThemeMode } from './theme';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type tThemeMode = 'light' | 'dark';
|
|
2
|
+
/** Pure ag-grid theme builder from mode. No React. Cached per mode: one shared
|
|
3
|
+
* theme object across all grids instead of one per component instance.
|
|
4
|
+
* dark uses the same parts and params as legacy GridStyleDefault for a consistent grid look. */
|
|
5
|
+
export declare function buildAgTheme(mode: tThemeMode): import("ag-grid-community").Theme<import("ag-grid-community").ThemeDefaultParams>;
|
|
6
|
+
/** Application theme; default is dark, as in production. */
|
|
7
|
+
export declare function useAgGridTheme(mode?: tThemeMode): import("ag-grid-community").Theme<import("ag-grid-community").ThemeDefaultParams>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// ag-grid theme. Pure builder + hook. Self-contained (no ThemeProvider in this project yet;
|
|
2
|
+
// when one appears, useAgGridTheme will start reading mode from it without a signature change).
|
|
3
|
+
import { colorSchemeDarkBlue, colorSchemeLight, iconSetMaterial, themeAlpine } from 'ag-grid-community';
|
|
4
|
+
import { tokens } from '../../styles/tokens';
|
|
5
|
+
const themeCache = {};
|
|
6
|
+
/** Pure ag-grid theme builder from mode. No React. Cached per mode: one shared
|
|
7
|
+
* theme object across all grids instead of one per component instance.
|
|
8
|
+
* dark uses the same parts and params as legacy GridStyleDefault for a consistent grid look. */
|
|
9
|
+
export function buildAgTheme(mode) {
|
|
10
|
+
return themeCache[mode] ??= themeAlpine
|
|
11
|
+
.withPart(mode == 'dark' ? colorSchemeDarkBlue : colorSchemeLight)
|
|
12
|
+
.withPart(iconSetMaterial)
|
|
13
|
+
.withParams({ ...tokens.grid, browserColorScheme: mode });
|
|
14
|
+
}
|
|
15
|
+
/** Application theme; default is dark, as in production. */
|
|
16
|
+
export function useAgGridTheme(mode = 'dark') {
|
|
17
|
+
return buildAgTheme(mode);
|
|
18
|
+
}
|
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
export declare const KeyDown: {
|
|
2
2
|
key: string;
|
|
3
3
|
};
|
|
4
|
-
export
|
|
5
|
-
|
|
4
|
+
export type AnyKeyDownApi = {
|
|
5
|
+
readonly key: string;
|
|
6
|
+
getKey(): string;
|
|
7
|
+
get(): string;
|
|
8
|
+
clear(): void;
|
|
9
|
+
reset(): void;
|
|
10
|
+
subscribe(listener: (key: string, event?: KeyboardEvent) => void): () => void;
|
|
11
|
+
on(listener: (key: string, event?: KeyboardEvent) => void): () => void;
|
|
12
|
+
};
|
|
13
|
+
export declare const keyDownApi: AnyKeyDownApi;
|
|
14
|
+
export declare function useAddDownAnyKey(options?: {
|
|
15
|
+
enabled?: boolean;
|
|
16
|
+
target?: Document | HTMLElement;
|
|
17
|
+
onKeyDown?: (key: string, event: KeyboardEvent) => void;
|
|
18
|
+
}): AnyKeyDownApi;
|
|
19
|
+
export declare const useKeyDown: typeof useAddDownAnyKey;
|
|
20
|
+
export declare const useAnyKey: typeof useAddDownAnyKey;
|
|
21
|
+
/** @deprecated Use `useKeyDown(options)` or `useAnyKey(options)`. */
|
|
22
|
+
export declare function addDownAnyKey(): AnyKeyDownApi;
|
|
23
|
+
/** @deprecated Use `useKeyDown(options)` or `useAnyKey(options)`. */
|
|
24
|
+
export declare const useAddDownAnyKeyOld: typeof useAddDownAnyKey;
|
|
@@ -1,21 +1,59 @@
|
|
|
1
|
-
import { useEffect } from "react";
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
2
|
import { renderBy } from "../../updateBy";
|
|
3
|
+
import { UseListen } from "wenay-common2";
|
|
3
4
|
export const KeyDown = {
|
|
4
5
|
key: ""
|
|
5
6
|
};
|
|
6
|
-
|
|
7
|
+
const [emitKeyDown, keyDownListen] = UseListen();
|
|
8
|
+
export const keyDownApi = {
|
|
9
|
+
get key() { return KeyDown.key; },
|
|
10
|
+
getKey() { return KeyDown.key; },
|
|
11
|
+
get() { return KeyDown.key; },
|
|
12
|
+
clear() {
|
|
13
|
+
KeyDown.key = "";
|
|
14
|
+
renderBy(KeyDown);
|
|
15
|
+
emitKeyDown("", undefined);
|
|
16
|
+
},
|
|
17
|
+
reset() {
|
|
18
|
+
keyDownApi.clear();
|
|
19
|
+
},
|
|
20
|
+
subscribe(listener) {
|
|
21
|
+
return keyDownListen.on(listener);
|
|
22
|
+
},
|
|
23
|
+
on(listener) {
|
|
24
|
+
return keyDownListen.on(listener);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
export function useAddDownAnyKey(options = {}) {
|
|
28
|
+
const { enabled = true, target } = options;
|
|
29
|
+
const onKeyDownRef = useRef(options.onKeyDown);
|
|
30
|
+
onKeyDownRef.current = options.onKeyDown;
|
|
7
31
|
useEffect(() => {
|
|
8
|
-
|
|
9
|
-
|
|
32
|
+
if (!enabled)
|
|
33
|
+
return;
|
|
34
|
+
const currentTarget = target ?? (typeof document !== "undefined" ? document : null);
|
|
35
|
+
if (!currentTarget)
|
|
36
|
+
return;
|
|
37
|
+
const func = (event) => {
|
|
38
|
+
if (!(event instanceof KeyboardEvent))
|
|
39
|
+
return;
|
|
40
|
+
KeyDown.key = event.key;
|
|
10
41
|
renderBy(KeyDown);
|
|
42
|
+
emitKeyDown(event.key, event);
|
|
43
|
+
onKeyDownRef.current?.(event.key, event);
|
|
11
44
|
};
|
|
12
|
-
|
|
45
|
+
currentTarget.addEventListener("keydown", func);
|
|
13
46
|
return () => {
|
|
14
|
-
|
|
47
|
+
currentTarget.removeEventListener("keydown", func);
|
|
15
48
|
};
|
|
16
|
-
}, []);
|
|
49
|
+
}, [enabled, target]);
|
|
50
|
+
return keyDownApi;
|
|
17
51
|
}
|
|
18
|
-
|
|
52
|
+
export const useKeyDown = useAddDownAnyKey;
|
|
53
|
+
export const useAnyKey = useAddDownAnyKey;
|
|
54
|
+
/** @deprecated Use `useKeyDown(options)` or `useAnyKey(options)`. */
|
|
19
55
|
export function addDownAnyKey() {
|
|
20
56
|
return useAddDownAnyKey();
|
|
21
57
|
}
|
|
58
|
+
/** @deprecated Use `useKeyDown(options)` or `useAnyKey(options)`. */
|
|
59
|
+
export const useAddDownAnyKeyOld = useAddDownAnyKey;
|
|
@@ -1,15 +1,33 @@
|
|
|
1
|
+
import React from "react";
|
|
1
2
|
export interface Position {
|
|
2
3
|
x: number;
|
|
3
4
|
y: number;
|
|
4
5
|
}
|
|
6
|
+
export type UseDraggableOptions = {
|
|
7
|
+
initialPosition?: Position;
|
|
8
|
+
holdMs?: number;
|
|
9
|
+
onDragEnd?: DragEndCallback;
|
|
10
|
+
onDragStart?: DragStartCallback;
|
|
11
|
+
};
|
|
5
12
|
export interface UseDraggableReturn {
|
|
6
|
-
position: Position;
|
|
13
|
+
readonly position: Position;
|
|
7
14
|
dragProps: {
|
|
8
15
|
onMouseDown: React.MouseEventHandler<HTMLDivElement>;
|
|
9
16
|
onTouchStart: React.TouchEventHandler<HTMLDivElement>;
|
|
10
17
|
};
|
|
11
18
|
}
|
|
19
|
+
export interface UseDraggableApi extends UseDraggableReturn {
|
|
20
|
+
props: UseDraggableReturn["dragProps"];
|
|
21
|
+
bind: UseDraggableReturn["dragProps"];
|
|
22
|
+
readonly positionRef: React.RefObject<Position>;
|
|
23
|
+
readonly isDragging: boolean;
|
|
24
|
+
getPosition(): Position;
|
|
25
|
+
setPosition(position: Position): void;
|
|
26
|
+
resetPosition(): void;
|
|
27
|
+
cancelDrag(): void;
|
|
28
|
+
}
|
|
12
29
|
type DragEndCallback = (finalPosition: Position) => void;
|
|
13
30
|
type DragStartCallback = () => void;
|
|
14
|
-
export declare function
|
|
31
|
+
export declare function useDraggableApi(options?: UseDraggableOptions): UseDraggableApi;
|
|
32
|
+
export declare function useDraggable(initialX?: number, initialY?: number, timeOut?: number, onDragEnd?: DragEndCallback, onDragStart?: DragStartCallback): UseDraggableApi;
|
|
15
33
|
export {};
|
|
@@ -1,134 +1,165 @@
|
|
|
1
|
-
import { useRef, useState
|
|
2
|
-
export function
|
|
3
|
-
const
|
|
1
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
export function useDraggableApi(options = {}) {
|
|
3
|
+
const { initialPosition = { x: 0, y: 0 }, holdMs = 500, onDragEnd, onDragStart } = options;
|
|
4
|
+
const [position, setPositionState] = useState(initialPosition);
|
|
5
|
+
const positionRef = useRef(position);
|
|
6
|
+
const holdMsRef = useRef(holdMs);
|
|
4
7
|
const offsetMouse = useRef({ x: 0, y: 0 });
|
|
5
8
|
const offsetTouch = useRef(null);
|
|
6
9
|
const [draggingMouse, setDraggingMouse] = useState(false);
|
|
7
10
|
const [draggingTouch, setDraggingTouch] = useState(false);
|
|
8
|
-
|
|
11
|
+
const draggingRef = useRef(false);
|
|
12
|
+
const onDragEndRef = useRef(onDragEnd);
|
|
13
|
+
const onDragStartRef = useRef(onDragStart);
|
|
9
14
|
const holdTimerMouse = useRef(null);
|
|
10
15
|
const holdTimerTouch = useRef(null);
|
|
11
|
-
const
|
|
12
|
-
|
|
16
|
+
const setPos = (p) => {
|
|
17
|
+
positionRef.current = p;
|
|
18
|
+
setPositionState(p);
|
|
19
|
+
};
|
|
20
|
+
holdMsRef.current = holdMs;
|
|
21
|
+
draggingRef.current = draggingMouse || draggingTouch;
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
onDragEndRef.current = onDragEnd;
|
|
24
|
+
onDragStartRef.current = onDragStart;
|
|
25
|
+
});
|
|
26
|
+
const cancelMouseHold = useMemo(() => function cancelMouseHold() {
|
|
27
|
+
if (holdTimerMouse.current != null) {
|
|
13
28
|
clearTimeout(holdTimerMouse.current);
|
|
14
29
|
holdTimerMouse.current = null;
|
|
15
30
|
}
|
|
16
|
-
document.removeEventListener("mouseup",
|
|
17
|
-
};
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
};
|
|
21
|
-
const handleMouseDown = (e) => {
|
|
22
|
-
e.preventDefault();
|
|
23
|
-
offsetMouse.current = {
|
|
24
|
-
x: e.clientX,
|
|
25
|
-
y: e.clientY,
|
|
26
|
-
};
|
|
27
|
-
if (timeOut) {
|
|
28
|
-
holdTimerMouse.current = window.setTimeout(() => {
|
|
29
|
-
setDraggingMouse(true);
|
|
30
|
-
holdTimerMouse.current = null;
|
|
31
|
-
document.removeEventListener("mouseup", handleMouseUpForHold);
|
|
32
|
-
}, timeOut);
|
|
33
|
-
document.addEventListener("mouseup", handleMouseUpForHold);
|
|
34
|
-
}
|
|
35
|
-
onDragStart?.();
|
|
36
|
-
};
|
|
37
|
-
const cancelTouchHold = () => {
|
|
38
|
-
if (holdTimerTouch.current) {
|
|
31
|
+
document.removeEventListener("mouseup", cancelMouseHold);
|
|
32
|
+
}, []);
|
|
33
|
+
const cancelTouchHold = useMemo(() => function cancelTouchHold() {
|
|
34
|
+
if (holdTimerTouch.current != null) {
|
|
39
35
|
clearTimeout(holdTimerTouch.current);
|
|
40
36
|
holdTimerTouch.current = null;
|
|
41
37
|
}
|
|
42
|
-
document.removeEventListener("touchend",
|
|
43
|
-
};
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
38
|
+
document.removeEventListener("touchend", cancelTouchHold);
|
|
39
|
+
}, []);
|
|
40
|
+
const bind = useMemo(() => ({
|
|
41
|
+
onMouseDown(e) {
|
|
42
|
+
e.preventDefault();
|
|
43
|
+
offsetMouse.current = { x: e.clientX, y: e.clientY };
|
|
44
|
+
if (holdMsRef.current > 0) {
|
|
45
|
+
holdTimerMouse.current = window.setTimeout(() => {
|
|
46
|
+
holdTimerMouse.current = null;
|
|
47
|
+
document.removeEventListener("mouseup", cancelMouseHold);
|
|
48
|
+
setDraggingMouse(true);
|
|
49
|
+
}, holdMsRef.current);
|
|
50
|
+
document.addEventListener("mouseup", cancelMouseHold);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
setDraggingMouse(true);
|
|
54
|
+
}
|
|
55
|
+
onDragStartRef.current?.();
|
|
56
|
+
},
|
|
57
|
+
onTouchStart(e) {
|
|
58
|
+
const touch = e.changedTouches[0];
|
|
59
|
+
if (!touch)
|
|
60
|
+
return;
|
|
61
|
+
offsetTouch.current = { x: touch.clientX, y: touch.clientY, id: touch.identifier };
|
|
62
|
+
if (holdMsRef.current > 0) {
|
|
63
|
+
holdTimerTouch.current = window.setTimeout(() => {
|
|
64
|
+
holdTimerTouch.current = null;
|
|
65
|
+
document.removeEventListener("touchend", cancelTouchHold);
|
|
66
|
+
setDraggingTouch(true);
|
|
67
|
+
}, holdMsRef.current);
|
|
68
|
+
document.addEventListener("touchend", cancelTouchHold);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
58
71
|
setDraggingTouch(true);
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
};
|
|
72
|
+
}
|
|
73
|
+
onDragStartRef.current?.();
|
|
74
|
+
},
|
|
75
|
+
}), [cancelMouseHold, cancelTouchHold]);
|
|
65
76
|
useEffect(() => {
|
|
66
|
-
if (draggingMouse)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
document.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
document.removeEventListener("mousemove", handleMouseMove);
|
|
88
|
-
document.removeEventListener("mouseup", handleMouseUp);
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
}, [draggingMouse, onDragEnd, position]);
|
|
77
|
+
if (!draggingMouse)
|
|
78
|
+
return;
|
|
79
|
+
const handleMouseMove = (e) => {
|
|
80
|
+
setPos({ x: e.clientX - offsetMouse.current.x, y: e.clientY - offsetMouse.current.y });
|
|
81
|
+
};
|
|
82
|
+
const handleMouseUp = () => {
|
|
83
|
+
document.removeEventListener("mousemove", handleMouseMove);
|
|
84
|
+
document.removeEventListener("mouseup", handleMouseUp);
|
|
85
|
+
const final = { ...positionRef.current };
|
|
86
|
+
setPos({ x: 0, y: 0 });
|
|
87
|
+
setDraggingMouse(false);
|
|
88
|
+
onDragEndRef.current?.(final);
|
|
89
|
+
offsetMouse.current = { x: 0, y: 0 };
|
|
90
|
+
};
|
|
91
|
+
document.addEventListener("mousemove", handleMouseMove);
|
|
92
|
+
document.addEventListener("mouseup", handleMouseUp);
|
|
93
|
+
return () => {
|
|
94
|
+
document.removeEventListener("mousemove", handleMouseMove);
|
|
95
|
+
document.removeEventListener("mouseup", handleMouseUp);
|
|
96
|
+
};
|
|
97
|
+
}, [draggingMouse]);
|
|
92
98
|
useEffect(() => {
|
|
93
|
-
if (draggingTouch)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (ended) {
|
|
109
|
-
setPosition({ x: 0, y: 0 });
|
|
110
|
-
document.removeEventListener("touchmove", handleTouchMove);
|
|
111
|
-
document.removeEventListener("touchend", handleTouchEnd);
|
|
112
|
-
setDraggingTouch(false);
|
|
113
|
-
if (onDragEnd) {
|
|
114
|
-
onDragEnd({ ...position });
|
|
115
|
-
}
|
|
116
|
-
offsetTouch.current = null;
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
document.addEventListener("touchmove", handleTouchMove);
|
|
120
|
-
document.addEventListener("touchend", handleTouchEnd);
|
|
121
|
-
return () => {
|
|
99
|
+
if (!draggingTouch)
|
|
100
|
+
return;
|
|
101
|
+
const handleTouchMove = (e) => {
|
|
102
|
+
if (!offsetTouch.current)
|
|
103
|
+
return;
|
|
104
|
+
const theTouch = Array.from(e.changedTouches).find((t) => t.identifier === offsetTouch.current?.id);
|
|
105
|
+
if (!theTouch)
|
|
106
|
+
return;
|
|
107
|
+
setPos({ x: theTouch.clientX - offsetTouch.current.x, y: theTouch.clientY - offsetTouch.current.y });
|
|
108
|
+
};
|
|
109
|
+
const handleTouchEnd = (e) => {
|
|
110
|
+
if (!offsetTouch.current)
|
|
111
|
+
return;
|
|
112
|
+
const ended = Array.from(e.changedTouches).find((t) => t.identifier === offsetTouch.current?.id);
|
|
113
|
+
if (ended) {
|
|
122
114
|
document.removeEventListener("touchmove", handleTouchMove);
|
|
123
115
|
document.removeEventListener("touchend", handleTouchEnd);
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
116
|
+
const final = { ...positionRef.current };
|
|
117
|
+
setPos({ x: 0, y: 0 });
|
|
118
|
+
setDraggingTouch(false);
|
|
119
|
+
onDragEndRef.current?.(final);
|
|
120
|
+
offsetTouch.current = null;
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
document.addEventListener("touchmove", handleTouchMove);
|
|
124
|
+
document.addEventListener("touchend", handleTouchEnd);
|
|
125
|
+
return () => {
|
|
126
|
+
document.removeEventListener("touchmove", handleTouchMove);
|
|
127
|
+
document.removeEventListener("touchend", handleTouchEnd);
|
|
128
|
+
};
|
|
129
|
+
}, [draggingTouch]);
|
|
130
|
+
useEffect(() => () => {
|
|
131
|
+
if (holdTimerMouse.current != null)
|
|
132
|
+
clearTimeout(holdTimerMouse.current);
|
|
133
|
+
if (holdTimerTouch.current != null)
|
|
134
|
+
clearTimeout(holdTimerTouch.current);
|
|
135
|
+
document.removeEventListener("mouseup", cancelMouseHold);
|
|
136
|
+
document.removeEventListener("touchend", cancelTouchHold);
|
|
137
|
+
}, [cancelMouseHold, cancelTouchHold]);
|
|
138
|
+
return useMemo(() => ({
|
|
139
|
+
get position() { return positionRef.current; },
|
|
140
|
+
get positionRef() { return positionRef; },
|
|
141
|
+
get isDragging() { return draggingRef.current; },
|
|
142
|
+
props: bind,
|
|
143
|
+
bind,
|
|
144
|
+
dragProps: bind,
|
|
145
|
+
getPosition() { return { ...positionRef.current }; },
|
|
146
|
+
setPosition: setPos,
|
|
147
|
+
resetPosition() { setPos({ x: 0, y: 0 }); },
|
|
148
|
+
cancelDrag() {
|
|
149
|
+
cancelMouseHold();
|
|
150
|
+
cancelTouchHold();
|
|
151
|
+
offsetTouch.current = null;
|
|
152
|
+
setDraggingMouse(false);
|
|
153
|
+
setDraggingTouch(false);
|
|
154
|
+
setPos({ x: 0, y: 0 });
|
|
132
155
|
},
|
|
133
|
-
};
|
|
156
|
+
}), [bind, cancelMouseHold, cancelTouchHold]);
|
|
157
|
+
}
|
|
158
|
+
export function useDraggable(initialX = 0, initialY = 0, timeOut = 500, onDragEnd, onDragStart) {
|
|
159
|
+
return useDraggableApi({
|
|
160
|
+
initialPosition: { x: initialX, y: initialY },
|
|
161
|
+
holdMs: timeOut,
|
|
162
|
+
onDragEnd,
|
|
163
|
+
onDragStart,
|
|
164
|
+
});
|
|
134
165
|
}
|
|
@@ -1,11 +1,31 @@
|
|
|
1
1
|
import React, { ReactElement } from "react";
|
|
2
2
|
export declare const StyleOtherRow: React.CSSProperties;
|
|
3
3
|
export declare const StyleOtherColum: React.CSSProperties;
|
|
4
|
-
export
|
|
5
|
-
ref?: React.RefObject<
|
|
6
|
-
outsideClick
|
|
4
|
+
export type UseOutsideOptions<T extends HTMLElement = HTMLDivElement> = {
|
|
5
|
+
ref?: React.RefObject<T | null>;
|
|
6
|
+
outsideClick?: () => void;
|
|
7
|
+
onOutside?: () => void;
|
|
7
8
|
status?: boolean;
|
|
8
|
-
|
|
9
|
+
enabled?: boolean;
|
|
10
|
+
};
|
|
11
|
+
export type UseOutsideApi<T extends HTMLElement = HTMLDivElement> = {
|
|
12
|
+
current: T | null;
|
|
13
|
+
ref: React.RefObject<T | null>;
|
|
14
|
+
props: {
|
|
15
|
+
ref: React.Ref<T>;
|
|
16
|
+
};
|
|
17
|
+
bind: {
|
|
18
|
+
ref: React.Ref<T>;
|
|
19
|
+
};
|
|
20
|
+
contains(target: EventTarget | null): boolean;
|
|
21
|
+
enable(): void;
|
|
22
|
+
disable(): void;
|
|
23
|
+
readonly enabled: boolean;
|
|
24
|
+
};
|
|
25
|
+
export declare function useOutsideApi<T extends HTMLElement = HTMLDivElement>(options: UseOutsideOptions<T>): UseOutsideApi<T>;
|
|
26
|
+
export declare function useOutside<T extends HTMLElement = HTMLDivElement>(options: UseOutsideOptions<T>): UseOutsideApi<T>;
|
|
27
|
+
/** @deprecated Use `useOutside(options)` and read `api.ref`, `api.props`, or `api.current`. */
|
|
28
|
+
export declare function useOutsideOld<T extends HTMLElement = HTMLDivElement>(options: UseOutsideOptions<T>): React.RefObject<T | null>;
|
|
9
29
|
type tChildrenFunc = (api: {
|
|
10
30
|
onClose: () => void;
|
|
11
31
|
}) => ReactElement | React.JSX.Element;
|