wenay-react2 1.0.40 → 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 +971 -0
- package/doc/PROJECT_FUNCTIONALITY.md +405 -0
- package/doc/PROJECT_RULES.md +80 -0
- package/doc/WENAY_REACT2_RENAMES.md +177 -0
- package/doc/changes/v1.0.35.md +26 -0
- package/doc/changes/v1.0.37.md +13 -0
- package/doc/changes/v1.0.38.md +48 -0
- package/doc/changes/v1.0.39.md +35 -0
- package/doc/changes/v1.0.40.md +15 -0
- package/doc/changes/v1.0.41.md +36 -0
- package/doc/changes/v1.0.42.md +26 -0
- package/doc/progress/README.md +14 -0
- package/doc/progress/architecture-fix-queue.md +74 -0
- package/doc/progress/hook-controller-opportunities.md +367 -0
- package/doc/progress/hook-extraction-audit.md +195 -0
- package/doc/progress/public-surface-normalization.md +368 -0
- package/doc/progress/style-system-normalization.md +121 -0
- package/doc/target/README.md +33 -0
- package/doc/target/my.md +81 -0
- package/doc/wenay-react2-1.0.8.tgz +0 -0
- package/doc/wenay-react2-rare.md +807 -0
- package/doc/wenay-react2.md +539 -0
- 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 +123 -5
- package/lib/common/src/logs/logs.js +218 -130
- package/lib/common/src/logs/logsController.d.ts +3 -0
- package/lib/common/src/styles/tokens.d.ts +7 -6
- package/lib/common/src/styles/tokens.js +8 -5
- 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/menuRight.css +29 -23
- package/lib/style/style.css +73 -10
- package/lib/style/tokens.css +8 -4
- package/package.json +3 -2
|
@@ -2,9 +2,11 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import React, { useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
3
3
|
import { Rnd } from "react-rnd";
|
|
4
4
|
import { createUpdateApi } from "../../../updateBy";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
import { floatingWindowMap } from "../../utils/persistedMaps";
|
|
6
|
+
import { useDraggableApi } from "../../hooks/useDraggable";
|
|
7
|
+
// Map of all popup window sizes; declared in utils/persistedMaps (memoryCache registry must not
|
|
8
|
+
// import the component layer) and re-exported here so the public surface is unchanged
|
|
9
|
+
export { floatingWindowMap };
|
|
8
10
|
// limit={{x:{min:0}, y:{min:0}}}
|
|
9
11
|
let k = 0;
|
|
10
12
|
const openWindows = { ar: [] };
|
|
@@ -237,8 +239,9 @@ export function useFloatingWindowController({ keyForSave: ks, position, size, zI
|
|
|
237
239
|
* Wrapper component around react-rnd.
|
|
238
240
|
* Provides dragging and resizing, an optional header, and a close button.
|
|
239
241
|
*/
|
|
240
|
-
export function FloatingWindowBase({ children, keyForSave: ks, position, size, overflow = true, zIndex = 9, onUpdate, disableDragging, className, header, moveOnlyHeader, limit, onCLickClose, sizeByWindow = true }) {
|
|
241
|
-
|
|
242
|
+
export function FloatingWindowBase({ children, keyForSave: ks, position, size, overflow = true, zIndex = 9, onUpdate, disableDragging, className, header, moveOnlyHeader, limit, onCLickClose, onClickClose, sizeByWindow = true }) {
|
|
243
|
+
const clickClose = onClickClose ?? onCLickClose;
|
|
244
|
+
// NOTE: no implicit limit for onClickClose windows. `limit` is parent-relative,
|
|
242
245
|
// so {y:{min:0}} pinned windows to their DOM parent's top: a window opened from a
|
|
243
246
|
// centered wrapper (ModalProvider / ModalWrapper at top:50%) could not be dragged
|
|
244
247
|
// above mid-screen. Keeping the window on screen is already handled by the
|
|
@@ -269,9 +272,9 @@ export function FloatingWindowBase({ children, keyForSave: ks, position, size, o
|
|
|
269
272
|
}, onMouseDown: controller.onWindowMouseDown, children: [moveOnlyHeader || header ? headerD : null, _jsxs("div", { className: "maxSize", style: { overflow: overflow ? "auto" : undefined }, children: [controller.dragging && (_jsx("div", { className: "maxSize", style: {
|
|
270
273
|
position: "absolute",
|
|
271
274
|
zIndex: controller.overlayZIndex
|
|
272
|
-
} })), typeof children === "function" ? children(controller.update) : children] }),
|
|
275
|
+
} })), typeof children === "function" ? children(controller.update) : children] }), clickClose && (_jsx("div", { className: "wenayCloseBtn wenayWndClose", title: "Close", style: {
|
|
273
276
|
zIndex: controller.overlayZIndex
|
|
274
|
-
}, onClick:
|
|
277
|
+
}, onClick: clickClose, children: _jsx("svg", { width: "12", height: "12", viewBox: "0 0 12 12", "aria-hidden": "true", children: _jsx("path", { d: "M2 2 L10 10 M10 2 L2 10", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" }) }) }, "323"))] }) }));
|
|
275
278
|
}
|
|
276
279
|
/**
|
|
277
280
|
* Wrapper component that lets a nested element be dragged
|
|
@@ -280,111 +283,50 @@ export function FloatingWindowBase({ children, keyForSave: ks, position, size, o
|
|
|
280
283
|
* Function only as a hook for parameter changes during movement, although it has its own component (for offset counting).
|
|
281
284
|
* Returns the distance traveled when moving the child element.
|
|
282
285
|
*/
|
|
283
|
-
export function DragBox({ children, onX, onY, x = 0, y = 0, right = false, last, dragging,
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
286
|
+
export function DragBox({ children, onX, onY, x = 0, y = 0, right = false, last, dragging: _dragging, // accepted for compatibility, was never read
|
|
287
|
+
onStart, onStop }) {
|
|
288
|
+
// Thin adapter over useDraggableApi (A7): same observable contract as the old
|
|
289
|
+
// bespoke loop - immediate start, per-tick imperative onX/onY with the delta from
|
|
290
|
+
// the press point, NO re-render per move tick, posRef keeps the last delta after
|
|
291
|
+
// release (reset happens on the next gesture start inside the hook).
|
|
288
292
|
const posRef = useRef(last?.current ?? { x, y });
|
|
289
|
-
const wasDragging = useRef(false);
|
|
290
293
|
const callbacksRef = useRef({ onX, onY, onStart, onStop });
|
|
291
294
|
callbacksRef.current = { onX, onY, onStart, onStop };
|
|
295
|
+
const api = useDraggableApi({
|
|
296
|
+
holdMs: 0,
|
|
297
|
+
trackState: false,
|
|
298
|
+
onDragStart() {
|
|
299
|
+
// per-gesture reset - without it repeated drags accumulated offset
|
|
300
|
+
posRef.current.x = 0;
|
|
301
|
+
posRef.current.y = 0;
|
|
302
|
+
callbacksRef.current.onStart?.();
|
|
303
|
+
},
|
|
304
|
+
onMove(p) {
|
|
305
|
+
// mutate in place: `last` shares this object (see layout effect below)
|
|
306
|
+
posRef.current.x = p.x;
|
|
307
|
+
posRef.current.y = p.y;
|
|
308
|
+
callbacksRef.current.onX?.(p.x);
|
|
309
|
+
callbacksRef.current.onY?.(p.y);
|
|
310
|
+
},
|
|
311
|
+
onDragEnd(final) {
|
|
312
|
+
posRef.current.x = final.x;
|
|
313
|
+
posRef.current.y = final.y;
|
|
314
|
+
callbacksRef.current.onStop?.();
|
|
315
|
+
},
|
|
316
|
+
});
|
|
292
317
|
useLayoutEffect(() => {
|
|
293
318
|
posRef.current.x = x;
|
|
294
319
|
posRef.current.y = y;
|
|
295
320
|
}, [x, y]);
|
|
296
|
-
useEffect(() => {
|
|
297
|
-
if (!draggingMouse && !draggingTouch) {
|
|
298
|
-
// onStop only after a real drag - previously it also fired on initial mount
|
|
299
|
-
if (wasDragging.current) {
|
|
300
|
-
wasDragging.current = false;
|
|
301
|
-
callbacksRef.current.onStop?.();
|
|
302
|
-
}
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
305
|
-
wasDragging.current = true;
|
|
306
|
-
if (draggingMouse) {
|
|
307
|
-
const handleMouseMove = (e) => {
|
|
308
|
-
const newX = e.clientX + offsetMouse.current.x;
|
|
309
|
-
const newY = e.clientY + offsetMouse.current.y;
|
|
310
|
-
posRef.current = { x: newX, y: newY };
|
|
311
|
-
callbacksRef.current.onX?.(newX);
|
|
312
|
-
callbacksRef.current.onY?.(newY);
|
|
313
|
-
};
|
|
314
|
-
const handleMouseUp = () => {
|
|
315
|
-
offsetMouse.current.x = 0;
|
|
316
|
-
offsetMouse.current.y = 0;
|
|
317
|
-
document.removeEventListener("mousemove", handleMouseMove);
|
|
318
|
-
document.removeEventListener("mouseup", handleMouseUp);
|
|
319
|
-
setDraggingMouse(false);
|
|
320
|
-
};
|
|
321
|
-
document.addEventListener("mousemove", handleMouseMove);
|
|
322
|
-
document.addEventListener("mouseup", handleMouseUp);
|
|
323
|
-
callbacksRef.current.onStart?.();
|
|
324
|
-
return () => {
|
|
325
|
-
document.removeEventListener("mousemove", handleMouseMove);
|
|
326
|
-
document.removeEventListener("mouseup", handleMouseUp);
|
|
327
|
-
};
|
|
328
|
-
}
|
|
329
|
-
if (draggingTouch) {
|
|
330
|
-
const handleTouchMove = (e) => {
|
|
331
|
-
if (!offsetTouch.current)
|
|
332
|
-
return;
|
|
333
|
-
const theTouch = Array.from(e.changedTouches).find((t) => t.identifier === offsetTouch.current?.id);
|
|
334
|
-
if (!theTouch)
|
|
335
|
-
return;
|
|
336
|
-
const newX = theTouch.clientX + offsetTouch.current.x;
|
|
337
|
-
const newY = theTouch.clientY + offsetTouch.current.y;
|
|
338
|
-
posRef.current = { x: newX, y: newY };
|
|
339
|
-
callbacksRef.current.onX?.(newX);
|
|
340
|
-
callbacksRef.current.onY?.(newY);
|
|
341
|
-
};
|
|
342
|
-
const handleTouchEnd = (e) => {
|
|
343
|
-
if (!offsetTouch.current)
|
|
344
|
-
return;
|
|
345
|
-
const ended = Array.from(e.changedTouches).find((t) => t.identifier === offsetTouch.current?.id);
|
|
346
|
-
if (ended) {
|
|
347
|
-
offsetTouch.current = null;
|
|
348
|
-
document.removeEventListener("touchmove", handleTouchMove);
|
|
349
|
-
document.removeEventListener("touchend", handleTouchEnd);
|
|
350
|
-
setDraggingTouch(false);
|
|
351
|
-
}
|
|
352
|
-
};
|
|
353
|
-
document.addEventListener("touchmove", handleTouchMove);
|
|
354
|
-
document.addEventListener("touchend", handleTouchEnd);
|
|
355
|
-
callbacksRef.current.onStart?.();
|
|
356
|
-
return () => {
|
|
357
|
-
document.removeEventListener("touchmove", handleTouchMove);
|
|
358
|
-
document.removeEventListener("touchend", handleTouchEnd);
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
}, [draggingMouse, draggingTouch]);
|
|
362
321
|
useLayoutEffect(() => {
|
|
363
322
|
if (last) {
|
|
364
323
|
last.current = posRef.current;
|
|
365
324
|
}
|
|
366
325
|
});
|
|
367
|
-
const handleMouseDown = (e) => {
|
|
368
|
-
e.preventDefault();
|
|
369
|
-
posRef.current.x = 0;
|
|
370
|
-
posRef.current.y = 0;
|
|
371
|
-
offsetMouse.current = { x: -e.clientX, y: -e.clientY };
|
|
372
|
-
setDraggingMouse(true);
|
|
373
|
-
};
|
|
374
|
-
const handleTouchStart = (e) => {
|
|
375
|
-
const t = e.changedTouches[0];
|
|
376
|
-
if (!t)
|
|
377
|
-
return;
|
|
378
|
-
// same per-gesture reset as mouse - without it repeated touch drags accumulated offset
|
|
379
|
-
posRef.current.x = 0;
|
|
380
|
-
posRef.current.y = 0;
|
|
381
|
-
offsetTouch.current = { x: -t.clientX, y: -t.clientY, id: t.identifier };
|
|
382
|
-
setDraggingTouch(true);
|
|
383
|
-
};
|
|
384
326
|
return (_jsx("div", { style: {
|
|
385
327
|
position: "absolute",
|
|
386
328
|
left: right ? undefined : 0,
|
|
387
329
|
right: right ? 0 : undefined,
|
|
388
330
|
top: 0
|
|
389
|
-
},
|
|
331
|
+
}, ...api.dragProps, children: children }));
|
|
390
332
|
}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { ResizableProps } from "re-resizable";
|
|
2
|
-
import {
|
|
3
|
-
type tSaveMap =
|
|
4
|
-
|
|
5
|
-
width?: number | string;
|
|
6
|
-
};
|
|
7
|
-
export declare const mapResiReact: ObservableMap<string, tSaveMap>;
|
|
2
|
+
import { mapResiReact, type ResizableSavedSize } from "../../utils/persistedMaps";
|
|
3
|
+
type tSaveMap = ResizableSavedSize;
|
|
4
|
+
export { mapResiReact };
|
|
8
5
|
type t3 = Pick<ResizableProps, "style" | "enable" | "onResize" | "children" | "size" | "maxWidth" | "maxHeight" | "minWidth" | "minHeight">;
|
|
9
6
|
export declare function FResizableReact({ style, onResize, enable, children, keyForSave, onResizeStop, size, minWidth, minHeight, maxWidth, maxHeight, moveWith, moveHeight }: t3 & {
|
|
10
7
|
keyForSave?: string;
|
|
@@ -13,4 +10,3 @@ export declare function FResizableReact({ style, onResize, enable, children, key
|
|
|
13
10
|
moveWith?: boolean;
|
|
14
11
|
moveHeight?: boolean;
|
|
15
12
|
}): import("react/jsx-runtime").JSX.Element;
|
|
16
|
-
export {};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Resizable } from "re-resizable";
|
|
3
|
-
import {
|
|
4
|
-
// Memory for all column sizes;
|
|
5
|
-
|
|
3
|
+
import { mapResiReact } from "../../utils/persistedMaps";
|
|
4
|
+
// Memory for all column sizes; declared in utils/persistedMaps (memoryCache registry must not
|
|
5
|
+
// import the component layer) and re-exported here so the public surface is unchanged
|
|
6
|
+
export { mapResiReact };
|
|
6
7
|
export function FResizableReact({ style, onResize, enable, children, keyForSave, onResizeStop, size = { height: 50, width: 50 }, minWidth, minHeight, maxWidth = "100%", maxHeight = "100%", moveWith = true, moveHeight = true }) {
|
|
7
8
|
let obj = size;
|
|
8
9
|
if (keyForSave) {
|
|
@@ -125,20 +125,24 @@ export function useRightMenuController({ elements, style, styles, position: init
|
|
|
125
125
|
...styles?.container,
|
|
126
126
|
display: 'flex'
|
|
127
127
|
};
|
|
128
|
+
// runs during render: guard for DOM-less environments (SSR/tests) - Infinity makes
|
|
129
|
+
// the clamp a no-op there; on the client the behavior is unchanged
|
|
130
|
+
const vw = typeof window != 'undefined' ? window.innerWidth : Infinity;
|
|
131
|
+
const vh = typeof window != 'undefined' ? window.innerHeight : Infinity;
|
|
128
132
|
if (position === 'left') {
|
|
129
|
-
computedStyle.left = Math.max(0, Math.min(computedX,
|
|
133
|
+
computedStyle.left = Math.max(0, Math.min(computedX, vw - 50));
|
|
130
134
|
computedStyle.right = 'auto';
|
|
131
135
|
}
|
|
132
136
|
else {
|
|
133
|
-
computedStyle.right = Math.max(0, Math.min(computedX,
|
|
137
|
+
computedStyle.right = Math.max(0, Math.min(computedX, vw - 50));
|
|
134
138
|
computedStyle.left = 'auto';
|
|
135
139
|
}
|
|
136
140
|
if (isTop) {
|
|
137
|
-
computedStyle.top = Math.max(0, Math.min(computedY,
|
|
141
|
+
computedStyle.top = Math.max(0, Math.min(computedY, vh - 50));
|
|
138
142
|
computedStyle.bottom = 'auto';
|
|
139
143
|
}
|
|
140
144
|
else {
|
|
141
|
-
computedStyle.bottom = Math.max(0, Math.min(computedY,
|
|
145
|
+
computedStyle.bottom = Math.max(0, Math.min(computedY, vh - 50));
|
|
142
146
|
computedStyle.top = 'auto';
|
|
143
147
|
}
|
|
144
148
|
return computedStyle;
|
|
@@ -8,5 +8,5 @@ export type MenuRightSavedState = {
|
|
|
8
8
|
y: number;
|
|
9
9
|
};
|
|
10
10
|
};
|
|
11
|
-
import {
|
|
12
|
-
export
|
|
11
|
+
import { mapRightMenu } from "../../utils/persistedMaps";
|
|
12
|
+
export { mapRightMenu };
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
// observable - memoryCache marks itself dirty on its mutations (drag end re-set()s the state)
|
|
3
|
-
|
|
1
|
+
import { mapRightMenu } from "../../utils/persistedMaps";
|
|
2
|
+
// observable - memoryCache marks itself dirty on its mutations (drag end re-set()s the state);
|
|
3
|
+
// declared in utils/persistedMaps (memoryCache registry must not import the component layer)
|
|
4
|
+
// and re-exported here so the public surface is unchanged
|
|
5
|
+
export { mapRightMenu };
|
|
@@ -5,7 +5,8 @@ import { createUpdateApi } from "../../../updateBy";
|
|
|
5
5
|
import { createModalElementStore } from "./Modal";
|
|
6
6
|
import { DragBox } from "../Dnd/FloatingWindow";
|
|
7
7
|
function useViewport() {
|
|
8
|
-
|
|
8
|
+
// lazy initializer + guard: window is absent in DOM-less environments (SSR/tests)
|
|
9
|
+
const [width, setWidth] = useState(() => typeof window != 'undefined' ? window.innerWidth : 0);
|
|
9
10
|
useEffect(() => {
|
|
10
11
|
const handleResize = () => setWidth(window.innerWidth);
|
|
11
12
|
window.addEventListener('resize', handleResize);
|
|
@@ -191,7 +192,8 @@ export function getApiLeftMenu() {
|
|
|
191
192
|
}, children: text ?? "" }));
|
|
192
193
|
};
|
|
193
194
|
const createDefaultElement = ({ color, children, textB }) => {
|
|
194
|
-
|
|
195
|
+
// guard: ApiLeftMenu is created at import time, so this must not touch window on a server
|
|
196
|
+
const viewportWidth = typeof window != 'undefined' ? window.innerWidth : 0;
|
|
195
197
|
return (_jsxs("div", { className: "blur", style: {
|
|
196
198
|
minHeight: "100vh",
|
|
197
199
|
width: viewportWidth > 800 ? "100%" : "calc(100vw - 50px)",
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { createContext, useContext,
|
|
3
|
-
import { createPortal } from 'react-dom';
|
|
4
|
-
import { OutsideClickArea } from "../../hooks/useOutside";
|
|
2
|
+
import { createContext, useContext, useMemo, useState } from 'react';
|
|
5
3
|
import { tokens } from "../../styles/tokens";
|
|
4
|
+
import { Overlay } from "../Overlay";
|
|
6
5
|
const noopModal = Object.assign(() => { }, { show() { }, open() { }, close() { }, replace() { }, set() { } });
|
|
7
6
|
const ModalContext = createContext(noopModal);
|
|
8
7
|
export const ModalProvider = ({ children, closeOnOutsideClick = true, closeOnEscape = true }) => {
|
|
@@ -16,21 +15,11 @@ export const ModalProvider = ({ children, closeOnOutsideClick = true, closeOnEsc
|
|
|
16
15
|
api.set = api.replace;
|
|
17
16
|
return api;
|
|
18
17
|
}, []);
|
|
19
|
-
|
|
20
|
-
if (!modal || !closeOnEscape)
|
|
21
|
-
return;
|
|
22
|
-
const onKeyDown = (e) => {
|
|
23
|
-
if (e.key == 'Escape')
|
|
24
|
-
setModal(null);
|
|
25
|
-
};
|
|
26
|
-
document.addEventListener('keydown', onKeyDown);
|
|
27
|
-
return () => document.removeEventListener('keydown', onKeyDown);
|
|
28
|
-
}, [modal, closeOnEscape]);
|
|
29
|
-
return (_jsxs(ModalContext.Provider, { value: modalApi, children: [children, modal && createPortal(_jsx("div", { style: {
|
|
18
|
+
return (_jsxs(ModalContext.Provider, { value: modalApi, children: [children, modal && (_jsx(Overlay, { scrimStyle: {
|
|
30
19
|
position: 'fixed', inset: 0, zIndex: tokens.zIndex.modal,
|
|
31
20
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
32
21
|
backgroundColor: 'var(--dlg-scrim, rgba(0, 0, 0, 0.5))'
|
|
33
|
-
},
|
|
34
|
-
|
|
22
|
+
}, onEscape: closeOnEscape ? () => setModal(null) : undefined, onOutsideClick: () => { if (closeOnOutsideClick)
|
|
23
|
+
setModal(null); }, children: modal }))] }));
|
|
35
24
|
};
|
|
36
25
|
export const useModal = () => useContext(ModalContext);
|
|
@@ -6,5 +6,29 @@ export declare class CResizeObserver {
|
|
|
6
6
|
add(element: Element, onResize: () => void): ObserveID;
|
|
7
7
|
delete(id: ObserveID): void;
|
|
8
8
|
}
|
|
9
|
+
/** Subscribe an element to the shared resize observer via the returned callback ref.
|
|
10
|
+
* `onResize` goes through a ref - a new function identity neither resubscribes nor is missed.
|
|
11
|
+
* The native observer fires once right after observe, so the first measurement is not skipped.
|
|
12
|
+
* `setResizeableElement` / `removeResizeableElement` below stay untouched - this is the
|
|
13
|
+
* hook-shaped entry over the same singleton. */
|
|
14
|
+
export declare function useResizeObserver<T extends Element = HTMLElement>(onResize: () => void): {
|
|
15
|
+
/** Callback ref - attach to the element to observe. */
|
|
16
|
+
ref: (el: T | null) => void;
|
|
17
|
+
/** The currently observed element (null while detached). */
|
|
18
|
+
element: () => T | null;
|
|
19
|
+
};
|
|
20
|
+
/** "I want the size -> I get the value/method": observed element's width/height as state
|
|
21
|
+
* (rounded, equality-guarded - a no-op resize does not re-render) plus a live `getSize()`
|
|
22
|
+
* getter for measurements that must not wait for a render. */
|
|
23
|
+
export declare function useElementSize<T extends Element = HTMLElement>(): {
|
|
24
|
+
ref: (el: T | null) => void;
|
|
25
|
+
element: () => T | null;
|
|
26
|
+
width: number;
|
|
27
|
+
height: number;
|
|
28
|
+
getSize: () => {
|
|
29
|
+
width: number;
|
|
30
|
+
height: number;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
9
33
|
export declare function setResizeableElement(el: HTMLElement): HTMLElement | undefined;
|
|
10
34
|
export declare function removeResizeableElement(el: HTMLElement): void;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useCallback, useRef, useState } from "react";
|
|
1
2
|
// Class for tracking element size changes
|
|
2
3
|
export class CResizeObserver {
|
|
3
4
|
#idMap = new WeakMap();
|
|
@@ -40,6 +41,54 @@ export class CResizeObserver {
|
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
43
|
const global_resizeObserver = new CResizeObserver();
|
|
44
|
+
/** Subscribe an element to the shared resize observer via the returned callback ref.
|
|
45
|
+
* `onResize` goes through a ref - a new function identity neither resubscribes nor is missed.
|
|
46
|
+
* The native observer fires once right after observe, so the first measurement is not skipped.
|
|
47
|
+
* `setResizeableElement` / `removeResizeableElement` below stay untouched - this is the
|
|
48
|
+
* hook-shaped entry over the same singleton. */
|
|
49
|
+
export function useResizeObserver(onResize) {
|
|
50
|
+
const cbRef = useRef(onResize);
|
|
51
|
+
cbRef.current = onResize;
|
|
52
|
+
const elRef = useRef(null);
|
|
53
|
+
const idRef = useRef(null);
|
|
54
|
+
const ref = useCallback((el) => {
|
|
55
|
+
if (idRef.current) {
|
|
56
|
+
global_resizeObserver.delete(idRef.current);
|
|
57
|
+
idRef.current = null;
|
|
58
|
+
}
|
|
59
|
+
elRef.current = el;
|
|
60
|
+
if (el)
|
|
61
|
+
idRef.current = global_resizeObserver.add(el, function emitResize() { cbRef.current(); });
|
|
62
|
+
}, []);
|
|
63
|
+
return {
|
|
64
|
+
/** Callback ref - attach to the element to observe. */
|
|
65
|
+
ref,
|
|
66
|
+
/** The currently observed element (null while detached). */
|
|
67
|
+
element: () => elRef.current,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/** "I want the size -> I get the value/method": observed element's width/height as state
|
|
71
|
+
* (rounded, equality-guarded - a no-op resize does not re-render) plus a live `getSize()`
|
|
72
|
+
* getter for measurements that must not wait for a render. */
|
|
73
|
+
export function useElementSize() {
|
|
74
|
+
const [size, setSize] = useState({ width: 0, height: 0 });
|
|
75
|
+
const obs = useResizeObserver(function readSize() {
|
|
76
|
+
const el = obs.element();
|
|
77
|
+
if (!el)
|
|
78
|
+
return;
|
|
79
|
+
const r = el.getBoundingClientRect();
|
|
80
|
+
const next = { width: Math.round(r.width), height: Math.round(r.height) };
|
|
81
|
+
setSize(prev => prev.width == next.width && prev.height == next.height ? prev : next);
|
|
82
|
+
});
|
|
83
|
+
const getSize = () => {
|
|
84
|
+
const el = obs.element();
|
|
85
|
+
if (!el)
|
|
86
|
+
return { width: 0, height: 0 };
|
|
87
|
+
const r = el.getBoundingClientRect();
|
|
88
|
+
return { width: r.width, height: r.height };
|
|
89
|
+
};
|
|
90
|
+
return { ref: obs.ref, element: obs.element, width: size.width, height: size.height, getSize };
|
|
91
|
+
}
|
|
43
92
|
const resizeableElementMap = new WeakMap();
|
|
44
93
|
function getWidth(el) {
|
|
45
94
|
return Math.ceil(el.clientWidth || el.getBoundingClientRect().width);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
export type OverlayProps = {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
/** Scrim look: a CSS class (SettingsDialog: `wenayDlgScrim`) and/or inline style
|
|
5
|
+
* (ModalProvider: flex-centered token scrim). The scrim div gets both verbatim. */
|
|
6
|
+
scrimClassName?: string;
|
|
7
|
+
scrimStyle?: React.CSSProperties;
|
|
8
|
+
/** Forwarded to the OutsideClickArea that wraps children (e.g. `wenayDlgOutside`). */
|
|
9
|
+
outsideClassName?: string;
|
|
10
|
+
outsideStatus?: boolean;
|
|
11
|
+
/** Attach a document-level Escape handler while mounted. Omit when the host owns
|
|
12
|
+
* its own keydown logic (SettingsDialog's two-stage Escape stays in its controller). */
|
|
13
|
+
onEscape?: () => void;
|
|
14
|
+
onOutsideClick?: () => void;
|
|
15
|
+
container?: Element;
|
|
16
|
+
};
|
|
17
|
+
/** INTERNAL (A9): the one portal+scrim+outside-click+Escape composition for the
|
|
18
|
+
* scrim-based modal systems - ModalProvider and SettingsDialog adapt to it.
|
|
19
|
+
* Deliberately NOT exported from the public barrels: apps keep using
|
|
20
|
+
* ModalProvider/useModal/SettingsDialog. The render-slot stores
|
|
21
|
+
* (createModalElementStore) and the LeftModal drawer are not overlays and stay
|
|
22
|
+
* separate. Keeping all scrim/portal DOM in this one leaf is also the seam for a
|
|
23
|
+
* future react-native view layer (headless state stays in the hosts). */
|
|
24
|
+
export declare function Overlay({ children, scrimClassName, scrimStyle, outsideClassName, outsideStatus, onEscape, onOutsideClick, container, }: OverlayProps): React.ReactPortal;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
|
+
import { createPortal } from 'react-dom';
|
|
4
|
+
import { OutsideClickArea } from '../hooks/useOutside';
|
|
5
|
+
/** INTERNAL (A9): the one portal+scrim+outside-click+Escape composition for the
|
|
6
|
+
* scrim-based modal systems - ModalProvider and SettingsDialog adapt to it.
|
|
7
|
+
* Deliberately NOT exported from the public barrels: apps keep using
|
|
8
|
+
* ModalProvider/useModal/SettingsDialog. The render-slot stores
|
|
9
|
+
* (createModalElementStore) and the LeftModal drawer are not overlays and stay
|
|
10
|
+
* separate. Keeping all scrim/portal DOM in this one leaf is also the seam for a
|
|
11
|
+
* future react-native view layer (headless state stays in the hosts). */
|
|
12
|
+
export function Overlay({ children, scrimClassName, scrimStyle, outsideClassName, outsideStatus = true, onEscape, onOutsideClick, container, }) {
|
|
13
|
+
// callbacks through refs: inline closures must not resubscribe the document listener
|
|
14
|
+
const callbacksRef = useRef({ onEscape, onOutsideClick });
|
|
15
|
+
callbacksRef.current = { onEscape, onOutsideClick };
|
|
16
|
+
const hasEscape = !!onEscape;
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (!hasEscape)
|
|
19
|
+
return;
|
|
20
|
+
const onKeyDown = (e) => {
|
|
21
|
+
if (e.key == 'Escape')
|
|
22
|
+
callbacksRef.current.onEscape?.();
|
|
23
|
+
};
|
|
24
|
+
document.addEventListener('keydown', onKeyDown);
|
|
25
|
+
return () => document.removeEventListener('keydown', onKeyDown);
|
|
26
|
+
}, [hasEscape]);
|
|
27
|
+
return createPortal(_jsx("div", { className: scrimClassName, style: scrimStyle, children: _jsx(OutsideClickArea, { outsideClick: () => callbacksRef.current.onOutsideClick?.(), status: outsideStatus, className: outsideClassName, children: children }) }), container ?? document.body);
|
|
28
|
+
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import React, { useEffect, useRef, useState } from "react";
|
|
3
|
-
import { createPortal } from "react-dom";
|
|
4
3
|
import { createUpdateApi, renderBy } from "../../../updateBy";
|
|
5
|
-
import { OutsideClickArea } from "../../hooks/useOutside";
|
|
6
4
|
import { FloatingWindowBase } from "../Dnd/FloatingWindow";
|
|
5
|
+
import { Overlay } from "../Overlay";
|
|
7
6
|
import { createSearchHistory } from "../../utils/searchHistory";
|
|
8
7
|
import { memoryGetOrCreate, memoryMarkDirty } from "../../utils/memoryStore";
|
|
9
8
|
const settingsDialogSize = { width: 820, height: 560 };
|
|
@@ -477,24 +476,24 @@ export function SettingsDialog(props) {
|
|
|
477
476
|
toggleExpanded(key);
|
|
478
477
|
} }), _jsx("span", { className: "wenayDlgTreeLabel", children: renderHighlightedLabel(node.section.name, searchTerms) })] }), hasChildren && isOpen && node.children.map(renderTreeNode)] }, key);
|
|
479
478
|
}
|
|
480
|
-
return _jsxs(_Fragment, { children: [_jsx("span", { role: "button", tabIndex: 0, "aria-label": props.trigger == null ? "Open settings" : undefined, onClick: openDialog, onKeyDown: onTriggerKeyDown, style: { display: "inline-block", cursor: "pointer" }, children: props.trigger ?? _jsx(DefaultSettingsTrigger, {}) }), open &&
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
479
|
+
return _jsxs(_Fragment, { children: [_jsx("span", { role: "button", tabIndex: 0, "aria-label": props.trigger == null ? "Open settings" : undefined, onClick: openDialog, onKeyDown: onTriggerKeyDown, style: { display: "inline-block", cursor: "pointer" }, children: props.trigger ?? _jsx(DefaultSettingsTrigger, {}) }), open && (_jsx(Overlay, { scrimClassName: "wenayDlgScrim", outsideClassName: "wenayDlgOutside", outsideStatus: open, onOutsideClick: () => setOpen(false), children: _jsx(FloatingWindowBase, { className: "wenayDlgWindow", size: settingsDialogSize, position: settingsDialogPosition, zIndex: 10000, moveOnlyHeader: true, overflow: false, onClickClose: () => setOpen(false), header: _jsx("div", { className: "wenayDlgHeader", children: "Settings" }), children: _jsxs("div", { className: classNames(["wenayDlg", navResizing && "wenayDlg_resizing"]), children: [_jsxs("div", { className: "wenayDlgNav", style: { width: navWidth }, children: [_jsxs("div", { className: "wenayDlgNavTop", children: [_jsxs("div", { ref: searchBoxRef, className: "wenayDlgSearchBox", onBlur: closeHistoryWhenSearchFocusLeaves, children: [_jsx("input", { ref: searchInputRef, className: "wenayDlgSearch", value: search, placeholder: "Search", "aria-label": "Search settings", onFocus: () => setHistoryOpen(searchHistory.length != 0), onChange: e => {
|
|
480
|
+
setSearch(e.currentTarget.value);
|
|
481
|
+
setHistoryOpen(searchHistory.length != 0);
|
|
482
|
+
}, onKeyDown: e => {
|
|
483
|
+
if (e.key == "Enter")
|
|
484
|
+
commitSearch();
|
|
485
|
+
if (e.key == "ArrowDown" && searchHistory[0]) {
|
|
486
|
+
e.preventDefault();
|
|
487
|
+
useHistoryItem(searchHistory[0]);
|
|
488
|
+
}
|
|
489
|
+
} }), searchHistory.length != 0 && _jsx("button", { type: "button", className: "wenayDlgSearchHistoryBtn", title: "Search history", "aria-label": "Search history", "aria-expanded": historyOpen, onClick: () => {
|
|
490
|
+
setHistoryOpen(v => !v);
|
|
491
|
+
searchInputRef.current?.focus();
|
|
492
|
+
}, children: _jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", "aria-hidden": "true", children: _jsx("path", { d: "M7 2a5 5 0 1 1-4.4 2.6M2 2v3h3M7 4v3l2 1" }) }) }), search != "" && _jsx("button", { type: "button", className: "wenayDlgSearchClear", title: "Clear search", "aria-label": "Clear search", onClick: () => {
|
|
493
|
+
setSearch("");
|
|
494
|
+
setHistoryOpen(searchHistory.length != 0);
|
|
495
|
+
searchInputRef.current?.focus();
|
|
496
|
+
}, children: _jsx("svg", { width: "12", height: "12", viewBox: "0 0 12 12", "aria-hidden": "true", children: _jsx("path", { d: "M2 2 L10 10 M10 2 L2 10", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" }) }) }), historyOpen && searchHistory.length != 0 && _jsxs("div", { className: "wenayDlgSearchHistory", role: "listbox", children: [searchHistory.map(item => _jsx("button", { type: "button", className: "wenayDlgSearchHistoryItem", onMouseDown: e => e.preventDefault(), onClick: () => useHistoryItem(item), children: item }, item)), _jsx("button", { type: "button", className: "wenayDlgSearchHistoryClear", onMouseDown: e => e.preventDefault(), onClick: () => {
|
|
497
|
+
clearHistory();
|
|
498
|
+
}, children: "Clear history" })] })] }), treeToolsVisible && _jsx("button", { className: classNames(["wenayDlgTreeTool", `wenayDlgTreeTool_${treeToolState}`]), type: "button", title: treeToolTitle, "aria-label": treeToolTitle, onClick: cycleTreeTool, children: _jsxs("span", { className: "wenayDlgTreeToolDots", "aria-hidden": "true", children: [_jsx("span", {}), _jsx("span", {}), _jsx("span", {})] }) })] }), _jsxs("div", { className: "wenayDlgTree", role: "tree", children: [tree.roots.map(renderTreeNode), firstVisibleNode == null && _jsx("div", { className: "wenayDlgNoResults", children: "No settings found" })] })] }), _jsx("div", { className: "wenayDlgDivider", role: "separator", "aria-orientation": "vertical", "aria-label": "Resize settings navigation", "aria-valuemin": settingsDialogNav.min, "aria-valuemax": settingsDialogNav.max, "aria-valuenow": navWidth, tabIndex: 0, title: "Drag to resize navigation; double-click to reset", onPointerDown: beginNavResize, onDoubleClick: () => commitNavWidth(settingsDialogNav.def), onKeyDown: onNavResizeKeyDown, children: _jsx("span", {}) }), _jsx("div", { className: "wenayDlgContent", children: current?.render() })] }) }) }))] });
|
|
500
499
|
}
|
|
@@ -3,6 +3,7 @@ import { useLayoutEffect, useRef, useState } from 'react';
|
|
|
3
3
|
import { listen as createListen } from 'wenay-common2';
|
|
4
4
|
import { createUpdateApi } from '../../../updateBy';
|
|
5
5
|
import { memoryGetOrCreate, memoryMarkDirty } from '../../utils/memoryStore';
|
|
6
|
+
import { pinFixedOrder, movedOrderWithFixed } from '../../utils/fixedOrder';
|
|
6
7
|
import { OutsideClickArea } from '../../hooks/useOutside';
|
|
7
8
|
import { useReorder } from '../../hooks/useReorder';
|
|
8
9
|
// Module singleton (closure + updateBy subscription, no React context) - same
|
|
@@ -114,8 +115,9 @@ export function createToolbar(opts) {
|
|
|
114
115
|
const ext = opts.source;
|
|
115
116
|
const sourceMode = ext ? (opts.sourceMode ?? 'orderVisible') : 'orderVisible';
|
|
116
117
|
// outside edits of the external config (grid drags...) must reach the
|
|
117
|
-
// toolbar's own subscribers too;
|
|
118
|
-
|
|
118
|
+
// toolbar's own subscribers too; lives until dispose() (repeated factories over
|
|
119
|
+
// one key used to leak a permanent listener per call - HMR, remounts)
|
|
120
|
+
const offSource = ext?.onChange?.(() => emitChange(normalize()));
|
|
119
121
|
function sameOrder(a, b) {
|
|
120
122
|
return a.length == b.length && a.every((k, i) => k == b[i]);
|
|
121
123
|
}
|
|
@@ -142,14 +144,11 @@ export function createToolbar(opts) {
|
|
|
142
144
|
const rawOrder = ext && sourceMode == 'order'
|
|
143
145
|
? mergeSourceOrder(Array.isArray(st.order) ? st.order : [], Array.isArray(extRaw?.order) ? extRaw.order : [], sourceKeys)
|
|
144
146
|
: Array.isArray(raw.order) ? raw.order : [];
|
|
145
|
-
const
|
|
147
|
+
const prelim = rawOrder.filter(k => known.has(k) && !opts.items.find(i => i.key == k)?.fixed);
|
|
146
148
|
for (const it of opts.items)
|
|
147
|
-
if (!it.fixed &&
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if (it.fixed)
|
|
151
|
-
order.splice(Math.min(i, order.length), 0, it.key);
|
|
152
|
-
});
|
|
149
|
+
if (!it.fixed && prelim.indexOf(it.key) == -1)
|
|
150
|
+
prelim.push(it.key);
|
|
151
|
+
const order = pinFixedOrder(prelim, opts.items);
|
|
153
152
|
const rawVisible = raw.visible && typeof raw.visible == 'object' ? raw.visible : {};
|
|
154
153
|
const visible = {};
|
|
155
154
|
for (const it of opts.items)
|
|
@@ -310,20 +309,9 @@ export function createToolbar(opts) {
|
|
|
310
309
|
const base = p.className ?? 'wenaySegBtn';
|
|
311
310
|
const activeCls = p.activeClassName ?? 'wenaySegBtnActive';
|
|
312
311
|
const byKey = new Map(opts.items.map(i => [i.key, i]));
|
|
313
|
-
/** Simulated commit: splice + the same fixed pinning as normalize(). */
|
|
312
|
+
/** Simulated commit: splice + the same fixed pinning as normalize() (shared utils/fixedOrder). */
|
|
314
313
|
function movedOrder(order, key, to) {
|
|
315
|
-
|
|
316
|
-
const from = next.indexOf(key);
|
|
317
|
-
if (from == -1)
|
|
318
|
-
return next;
|
|
319
|
-
next.splice(from, 1);
|
|
320
|
-
next.splice(Math.max(0, Math.min(next.length, to)), 0, key);
|
|
321
|
-
const res = next.filter(k => !byKey.get(k)?.fixed);
|
|
322
|
-
opts.items.forEach(function pinFixed(it, i) {
|
|
323
|
-
if (it.fixed)
|
|
324
|
-
res.splice(Math.min(i, res.length), 0, it.key);
|
|
325
|
-
});
|
|
326
|
-
return res;
|
|
314
|
+
return movedOrderWithFixed(order, key, to, opts.items);
|
|
327
315
|
}
|
|
328
316
|
const reorder = useReorder({
|
|
329
317
|
order: cfg.order,
|
|
@@ -354,9 +342,14 @@ export function createToolbar(opts) {
|
|
|
354
342
|
}, onTouchStart: it.fixed ? undefined : r.props.onTouchStart, children: [_jsx("input", { type: 'checkbox', disabled: it.fixed, checked: cfg.visible[k] != false, onChange: () => setConfig({ ...cfg, visible: { ...cfg.visible, [k]: !(cfg.visible[k] != false) } }) }), _jsx("span", { className: 'wenayTbIcon', children: toolbarItemIcon(it) }), _jsx("span", { className: 'wenayTbRowTitle', children: it.title }), !it.fixed && _jsx("div", { className: 'wenayTbHandle', tabIndex: 0, title: 'Drag or arrow keys to reorder', onKeyDown: e => onHandleKey(k, e), children: "\u283F" })] }, k);
|
|
355
343
|
}) }), _jsxs("div", { className: 'wenayTbRow wenayTbRowMeta', children: [_jsx("input", { type: 'checkbox', checked: cfg.visible[SETTINGS_KEY] != false, onChange: () => setConfig({ ...cfg, visible: { ...cfg.visible, [SETTINGS_KEY]: !(cfg.visible[SETTINGS_KEY] != false) } }) }), _jsx("span", { className: 'wenayTbIcon', children: settingsIcon() }), _jsx("span", { className: 'wenayTbRowTitle', children: settingsTitle() })] }), opts.resetItem !== false && _jsxs("div", { className: 'wenayTbRow wenayTbRowMeta', children: [_jsx("input", { type: 'checkbox', checked: cfg.visible[RESET_KEY] != false, onChange: () => setConfig({ ...cfg, visible: { ...cfg.visible, [RESET_KEY]: !(cfg.visible[RESET_KEY] != false) } }) }), _jsx("span", { className: 'wenayTbIcon', children: resetIcon() }), _jsx("span", { className: 'wenayTbRowTitle', children: resetTitle() }), _jsx("button", { type: 'button', className: 'wenayTbMetaAction', title: resetTitle(), onClick: () => reset(), children: resetIcon() })] })] });
|
|
356
344
|
}
|
|
345
|
+
/** Release the external-source subscription. The persisted config itself stays -
|
|
346
|
+
* dispose() is about listener lifetime (HMR, remounts), not about state. */
|
|
347
|
+
function dispose() {
|
|
348
|
+
offSource?.();
|
|
349
|
+
}
|
|
357
350
|
return {
|
|
358
351
|
Bar,
|
|
359
352
|
Settings,
|
|
360
|
-
api: { useConfig, useItems, getConfig, setConfig, setOrder, show, setDensity, showSettings, showReset, reset, onChange },
|
|
353
|
+
api: { useConfig, useItems, getConfig, setConfig, setOrder, show, setDensity, showSettings, showReset, reset, onChange, dispose },
|
|
361
354
|
};
|
|
362
355
|
}
|