wenay-react2 1.0.41 → 1.0.42
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/doc/EXAMPLE_USAGE.md +58 -2
- package/doc/PROJECT_FUNCTIONALITY.md +5 -1
- package/doc/changes/v1.0.41.md +23 -0
- package/doc/changes/v1.0.42.md +26 -0
- package/doc/progress/architecture-fix-queue.md +74 -0
- package/doc/progress/style-system-normalization.md +5 -5
- package/doc/target/my.md +39 -67
- package/doc/wenay-react2-rare.md +118 -13
- package/doc/wenay-react2.md +75 -11
- package/lib/common/api.d.ts +1 -0
- package/lib/common/api.js +8 -4
- package/lib/common/src/components/Dnd/DragArea.d.ts +5 -0
- package/lib/common/src/components/Dnd/DragArea.js +5 -1
- package/lib/common/src/components/Dnd/FloatingWindow.d.ts +9 -5
- package/lib/common/src/components/Dnd/FloatingWindow.js +39 -97
- package/lib/common/src/components/Dnd/Resizable.d.ts +3 -7
- package/lib/common/src/components/Dnd/Resizable.js +4 -3
- package/lib/common/src/components/Menu/RightMenu.js +8 -4
- package/lib/common/src/components/Menu/RightMenuStore.d.ts +2 -2
- package/lib/common/src/components/Menu/RightMenuStore.js +5 -3
- package/lib/common/src/components/Modal/LeftModal.js +4 -2
- package/lib/common/src/components/Modal/ModalContextProvider.js +5 -16
- package/lib/common/src/components/MyResizeObserver.d.ts +24 -0
- package/lib/common/src/components/MyResizeObserver.js +49 -0
- package/lib/common/src/components/Overlay.d.ts +24 -0
- package/lib/common/src/components/Overlay.js +28 -0
- package/lib/common/src/components/Settings/SettingsDialog.js +21 -22
- package/lib/common/src/components/Toolbar/Toolbar.d.ts +1 -0
- package/lib/common/src/components/Toolbar/Toolbar.js +16 -23
- package/lib/common/src/grid/columnState/ColumnsMenu.js +4 -13
- package/lib/common/src/grid/columnState/columnGrid.d.ts +103 -0
- package/lib/common/src/grid/columnState/columnGrid.js +231 -0
- package/lib/common/src/grid/columnState/columnState.js +10 -9
- package/lib/common/src/grid/columnState/index.js +3 -0
- package/lib/common/src/hooks/useDraggable.d.ts +8 -0
- package/lib/common/src/hooks/useDraggable.js +13 -4
- package/lib/common/src/hooks/useOutside.d.ts +4 -1
- package/lib/common/src/hooks/useOutside.js +2 -1
- package/lib/common/src/logs/logs.d.ts +96 -5
- package/lib/common/src/logs/logs.js +147 -108
- package/lib/common/src/logs/logsController.d.ts +3 -0
- package/lib/common/src/utils/cache.d.ts +12 -0
- package/lib/common/src/utils/cache.js +0 -0
- package/lib/common/src/utils/fixedOrder.d.ts +15 -0
- package/lib/common/src/utils/fixedOrder.js +30 -0
- package/lib/common/src/utils/index.d.ts +2 -0
- package/lib/common/src/utils/index.js +2 -0
- package/lib/common/src/utils/memoryStore.d.ts +3 -6
- package/lib/common/src/utils/memoryStore.js +1 -3
- package/lib/common/src/utils/persistedMaps.d.ts +16 -0
- package/lib/common/src/utils/persistedMaps.js +5 -0
- package/lib/common/src/utils/searchHistory.js +7 -6
- package/lib/common/src/utils/structEqual.d.ts +12 -0
- package/lib/common/src/utils/structEqual.js +40 -0
- package/lib/common/updateBy.d.ts +3 -0
- package/lib/common/updateBy.js +57 -22
- package/lib/style/style.css +58 -1
- package/package.json +2 -2
package/lib/common/updateBy.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useLayoutEffect, useSyncExternalStore } from "react";
|
|
1
|
+
import React, { useCallback, useLayoutEffect, useSyncExternalStore } from "react";
|
|
2
2
|
import { listen as createListen, waitRun } from "wenay-common2";
|
|
3
3
|
export const map3 = new WeakMap();
|
|
4
4
|
export const mapWait = new Map();
|
|
@@ -14,34 +14,59 @@ function getUpdateListen(obj) {
|
|
|
14
14
|
function getObserverState(obj) {
|
|
15
15
|
let state = map3.get(obj);
|
|
16
16
|
if (!state) {
|
|
17
|
-
state = { listeners: new Set(), version: 0 };
|
|
17
|
+
state = { listeners: new Set(), callbacks: new Set(), version: 0, running: false, pending: false };
|
|
18
18
|
map3.set(obj, state);
|
|
19
19
|
}
|
|
20
20
|
return state;
|
|
21
21
|
}
|
|
22
|
-
// reverse/lastOnly действуют только на React-подписчиков (
|
|
23
|
-
// императивные
|
|
24
|
-
function
|
|
22
|
+
// reverse/lastOnly действуют только на React-подписчиков (useSyncExternalStore);
|
|
23
|
+
// императивные f-колбэки вызываются всегда все, в порядке подписки, ДО React-нотификаторов
|
|
24
|
+
function runTriggerPass(obj, state, reverse, lastOnly) {
|
|
25
25
|
const listen = updateListens.get(obj);
|
|
26
|
-
let state = map3.get(obj);
|
|
27
|
-
if ((!state || state.listeners.size === 0) && !listen?.[1].count())
|
|
28
|
-
return;
|
|
29
|
-
state ??= getObserverState(obj);
|
|
30
26
|
state.version += 1;
|
|
31
|
-
|
|
27
|
+
// императивные f-колбэки: всегда все, в порядке подписки
|
|
28
|
+
Array.from(state.callbacks).forEach(callback => callback(obj));
|
|
29
|
+
const listenersArray = Array.from(state.listeners);
|
|
32
30
|
if (lastOnly) {
|
|
33
31
|
const last = listenersArray.at(-1);
|
|
34
32
|
if (last)
|
|
35
33
|
last(obj);
|
|
36
|
-
listen?.[0](obj);
|
|
37
|
-
return;
|
|
38
34
|
}
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
else {
|
|
36
|
+
if (reverse) {
|
|
37
|
+
listenersArray.reverse();
|
|
38
|
+
}
|
|
39
|
+
listenersArray.forEach(listener => listener(obj));
|
|
41
40
|
}
|
|
42
|
-
listenersArray.forEach(listener => listener(obj));
|
|
43
41
|
listen?.[0](obj);
|
|
44
42
|
}
|
|
43
|
+
const MAX_TRIGGER_PASSES = 100;
|
|
44
|
+
function triggerUpdate(obj, reverse = false, lastOnly = false) {
|
|
45
|
+
const listen = updateListens.get(obj);
|
|
46
|
+
let state = map3.get(obj);
|
|
47
|
+
if ((!state || (state.listeners.size === 0 && state.callbacks.size === 0)) && !listen?.[1].count())
|
|
48
|
+
return;
|
|
49
|
+
state ??= getObserverState(obj);
|
|
50
|
+
// реентерабельность: синхронный renderBy по тому же объекту изнутри слушателя
|
|
51
|
+
// коалесцируется — помечаем pending и выходим, текущая итерация сделает ещё проход
|
|
52
|
+
if (state.running) {
|
|
53
|
+
state.pending = true;
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
state.running = true;
|
|
57
|
+
try {
|
|
58
|
+
for (let pass = 0; pass < MAX_TRIGGER_PASSES; pass++) {
|
|
59
|
+
state.pending = false;
|
|
60
|
+
runTriggerPass(obj, state, reverse, lastOnly);
|
|
61
|
+
if (!state.pending)
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
state.running = false;
|
|
67
|
+
state.pending = false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
45
70
|
function schedule(a, ms, reverse = false, lastOnly = false) {
|
|
46
71
|
if (ms) {
|
|
47
72
|
(mapWait.get(a) || mapWait.set(a, waitRun()).get(a))
|
|
@@ -63,29 +88,39 @@ export function renderByLast(a, ms) {
|
|
|
63
88
|
schedule(a, ms, false, true);
|
|
64
89
|
}
|
|
65
90
|
export function useUpdateBy(a, f) {
|
|
91
|
+
// f прокидывается через ref: inline-колбэк (новая identity на каждый рендер)
|
|
92
|
+
// не должен вызывать переподписку — регистрируется одна стабильная обёртка на (a)
|
|
93
|
+
const fRef = React.useRef(f);
|
|
94
|
+
fRef.current = f;
|
|
95
|
+
const hasF = !!f;
|
|
66
96
|
// стабильный subscribe: без useCallback React переподписывается на каждый рендер,
|
|
67
97
|
// а удаление state из map3 при этом сбрасывало бы version → лишний форс-ререндер
|
|
68
98
|
const subscribe = useCallback((listener) => {
|
|
69
|
-
if (
|
|
99
|
+
if (hasF)
|
|
70
100
|
return () => { };
|
|
71
101
|
const state = getObserverState(a);
|
|
72
102
|
state.listeners.add(listener);
|
|
73
103
|
return () => {
|
|
74
104
|
state.listeners.delete(listener);
|
|
75
105
|
};
|
|
76
|
-
}, [a,
|
|
106
|
+
}, [a, hasF]);
|
|
77
107
|
useSyncExternalStore(subscribe,
|
|
78
108
|
// getSnapshot must be pure: only read the version, subscribe creates the state
|
|
79
|
-
() => (
|
|
109
|
+
() => (hasF ? 0 : (map3.get(a)?.version ?? 0)));
|
|
80
110
|
useLayoutEffect(() => {
|
|
81
|
-
if (!
|
|
111
|
+
if (!hasF)
|
|
82
112
|
return;
|
|
83
113
|
const state = getObserverState(a);
|
|
84
|
-
|
|
114
|
+
// обёртка передаёт тот же мутированный объект — для ветки Dispatch<SetStateAction<T>>
|
|
115
|
+
// setState(тот же ref) React бэйлаутит без ререндера (см. UpdateCallback)
|
|
116
|
+
const wrapper = (obj) => {
|
|
117
|
+
fRef.current?.(obj);
|
|
118
|
+
};
|
|
119
|
+
state.callbacks.add(wrapper);
|
|
85
120
|
return () => {
|
|
86
|
-
state.
|
|
121
|
+
state.callbacks.delete(wrapper);
|
|
87
122
|
};
|
|
88
|
-
}, [a,
|
|
123
|
+
}, [a, hasF]);
|
|
89
124
|
}
|
|
90
125
|
export function updateBy(a, f) {
|
|
91
126
|
useUpdateBy(a, f);
|
package/lib/style/style.css
CHANGED
|
@@ -751,6 +751,63 @@ body {
|
|
|
751
751
|
overflow: auto;
|
|
752
752
|
}
|
|
753
753
|
|
|
754
|
+
/* Column grid kit layout (createColumnGrid.View). */
|
|
755
|
+
.wenayColumnGrid {
|
|
756
|
+
position: relative;
|
|
757
|
+
display: grid;
|
|
758
|
+
gap: var(--cols-grid-gap, 8px);
|
|
759
|
+
min-width: 0;
|
|
760
|
+
}
|
|
761
|
+
.wenayColumnGridControls,
|
|
762
|
+
.wenayColumnGridBody {
|
|
763
|
+
min-width: 0;
|
|
764
|
+
}
|
|
765
|
+
.wenayColumnGridControls_dots {
|
|
766
|
+
position: absolute;
|
|
767
|
+
left: var(--cols-grid-dots-x, 10px);
|
|
768
|
+
right: var(--cols-grid-dots-x, 10px);
|
|
769
|
+
bottom: var(--cols-grid-dots-bottom, 10px);
|
|
770
|
+
z-index: 3;
|
|
771
|
+
}
|
|
772
|
+
.wenayColumnGrid_cards.wenayColumnGrid_controls_dots .wenayCardListItem:last-child {
|
|
773
|
+
padding-bottom: var(--cols-grid-dots-card-bottom-padding, 92px);
|
|
774
|
+
}
|
|
775
|
+
.wenayColumnGridDots {
|
|
776
|
+
padding: var(--cols-grid-dots-padding, 6px 8px 8px);
|
|
777
|
+
border: 1px solid transparent;
|
|
778
|
+
border-radius: 8px;
|
|
779
|
+
background: transparent;
|
|
780
|
+
box-shadow: none;
|
|
781
|
+
backdrop-filter: none;
|
|
782
|
+
}
|
|
783
|
+
.wenayColumnGridDots .wenayColDotsHead {
|
|
784
|
+
justify-content: flex-end;
|
|
785
|
+
gap: 0;
|
|
786
|
+
margin-bottom: 2px;
|
|
787
|
+
font-size: 11px;
|
|
788
|
+
}
|
|
789
|
+
.wenayColumnGridDots .wenayColDotsTrack {
|
|
790
|
+
height: 48px;
|
|
791
|
+
margin: 0 16px;
|
|
792
|
+
}
|
|
793
|
+
.wenayColumnGridDots .wenayColDotsMeta,
|
|
794
|
+
.wenayColumnGridDots .wenayColDotsSpacer {
|
|
795
|
+
display: none;
|
|
796
|
+
}
|
|
797
|
+
.wenayColumnGridDots .wenayColDotsSort {
|
|
798
|
+
width: 32px;
|
|
799
|
+
height: 28px;
|
|
800
|
+
padding: 0;
|
|
801
|
+
font-size: 0;
|
|
802
|
+
opacity: 0.82;
|
|
803
|
+
background: transparent;
|
|
804
|
+
}
|
|
805
|
+
.wenayColumnGridDots .wenayColDotsSort::before {
|
|
806
|
+
content: "⇅";
|
|
807
|
+
font-size: 16px;
|
|
808
|
+
line-height: 1;
|
|
809
|
+
}
|
|
810
|
+
|
|
754
811
|
/* Column state compact menu (ColumnsMenu/MenuStrip). Visual defaults moved from
|
|
755
812
|
component inline styles; runtime drag transforms remain inline. */
|
|
756
813
|
.wenayColsMenu,
|
|
@@ -1168,4 +1225,4 @@ body {
|
|
|
1168
1225
|
.wenaySegBtnActive {
|
|
1169
1226
|
background: rgba(255, 255, 255, 0.16);
|
|
1170
1227
|
color: #fff;
|
|
1171
|
-
}
|
|
1228
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wenay-react2",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.42",
|
|
4
4
|
"description": "Common react",
|
|
5
5
|
"strict": true,
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"react": "^19.2.0",
|
|
25
25
|
"react-dom": "^19.2.0",
|
|
26
26
|
"react-rnd": "^10.5.3",
|
|
27
|
-
"wenay-common2": "^1.0.
|
|
27
|
+
"wenay-common2": "^1.0.70"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
30
|
"react": "^19.2.0",
|