wenay-react2 1.0.35 → 1.0.37
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 +8 -53
- package/lib/common/api.d.ts +28 -28
- package/lib/common/api.js +31 -31
- package/lib/common/src/components/Buttons/MiniButton.d.ts +1 -3
- package/lib/common/src/components/Buttons/MiniButton.js +1 -3
- package/lib/common/src/components/Dnd/{RNDFunc.d.ts → DragArea.d.ts} +1 -1
- package/lib/common/src/components/Dnd/{RNDFunc.js → DragArea.js} +1 -1
- package/lib/common/src/components/Dnd/{RNDFunc3.d.ts → FloatingWindow.d.ts} +7 -7
- package/lib/common/src/components/Dnd/{RNDFunc3.js → FloatingWindow.js} +10 -10
- package/lib/common/src/components/Dnd/OutlineDragDemo.d.ts +1 -0
- package/lib/common/src/components/Dnd/{DraggableOutlineDiv.js → OutlineDragDemo.js} +1 -2
- package/lib/common/src/components/Dnd/Resizable.js +1 -1
- package/lib/common/src/components/Dnd/index.d.ts +3 -3
- package/lib/common/src/components/Dnd/index.js +3 -3
- package/lib/common/src/components/Input.d.ts +5 -5
- package/lib/common/src/components/Input.js +10 -10
- package/lib/common/src/components/Menu/RightMenu.d.ts +25 -5
- package/lib/common/src/components/Menu/RightMenu.js +27 -19
- package/lib/common/src/components/Menu/RightMenuStore.d.ts +2 -2
- package/lib/common/src/components/Menu/RightMenuStore.js +1 -1
- package/lib/common/src/components/Modal/LeftModal.js +4 -4
- package/lib/common/src/components/Modal/Modal.d.ts +11 -17
- package/lib/common/src/components/Modal/Modal.js +15 -21
- package/lib/common/src/components/Modal/ModalContextProvider.d.ts +0 -6
- package/lib/common/src/components/Modal/ModalContextProvider.js +2 -9
- package/lib/common/src/components/Other.d.ts +2 -2
- package/lib/common/src/components/Other.js +5 -5
- package/lib/common/src/components/Parameters.d.ts +3 -3
- package/lib/common/src/components/Parameters.js +3 -3
- package/lib/common/src/components/{ParametersEngine.d.ts → ParamsEditor.d.ts} +1 -1
- package/lib/common/src/components/{ParametersEngine.js → ParamsEditor.js} +10 -10
- package/lib/common/src/components/Settings/SettingsDialog.d.ts +4 -4
- package/lib/common/src/components/Toolbar/Toolbar.d.ts +50 -37
- package/lib/common/src/components/Toolbar/Toolbar.js +60 -23
- package/lib/common/src/components/UiSlot/UiSlot.d.ts +3 -3
- package/lib/common/src/components/UiSlot/UiSlot.js +6 -6
- package/lib/common/src/grid/agGrid4/agGrid4.d.ts +4 -4
- package/lib/common/src/grid/agGrid4/agGrid4.js +6 -6
- package/lib/common/src/grid/agGrid4/index.d.ts +3 -3
- package/lib/common/src/grid/agGrid4/index.js +1 -1
- package/lib/common/src/grid/agGrid4/theme.d.ts +3 -3
- package/lib/common/src/grid/columnState/CardList.d.ts +11 -0
- package/lib/common/src/grid/columnState/CardList.js +28 -0
- package/lib/common/src/grid/columnState/ColumnDots.d.ts +9 -0
- package/lib/common/src/grid/columnState/ColumnDots.js +121 -0
- package/lib/common/src/grid/columnState/ColumnsMenu.d.ts +65 -0
- package/lib/common/src/grid/columnState/ColumnsMenu.js +123 -0
- package/lib/common/src/grid/columnState/columnState.d.ts +113 -0
- package/lib/common/src/grid/columnState/columnState.js +284 -0
- package/lib/common/src/grid/columnState/index.d.ts +4 -0
- package/lib/common/src/grid/columnState/index.js +4 -0
- package/lib/common/src/hooks/index.d.ts +1 -1
- package/lib/common/src/hooks/index.js +1 -1
- package/lib/common/src/hooks/useKeyboard.d.ts +18 -0
- package/lib/common/src/hooks/{useAddDownAnyKey.js → useKeyboard.js} +16 -24
- package/lib/common/src/hooks/useObserveStore.d.ts +12 -12
- package/lib/common/src/hooks/useObserveStore.js +2 -2
- package/lib/common/src/hooks/useOutside.d.ts +12 -19
- package/lib/common/src/hooks/useOutside.js +9 -16
- package/lib/common/src/hooks/useReorder.d.ts +4 -4
- package/lib/common/src/hooks/useReorderBoard.d.ts +15 -15
- package/lib/common/src/hooks/useReplay.d.ts +9 -9
- package/lib/common/src/hooks/useReplay.js +7 -7
- package/lib/common/src/logs/logs.d.ts +3 -3
- package/lib/common/src/logs/logs.js +11 -13
- package/lib/common/src/logs/{logs3.d.ts → logsContext.d.ts} +4 -4
- package/lib/common/src/logs/{logs3.js → logsContext.js} +4 -4
- package/lib/common/src/menu/menu.d.ts +16 -16
- package/lib/common/src/menu/menu.js +12 -11
- package/lib/common/src/menu/menuMouse.d.ts +71 -13
- package/lib/common/src/menu/menuMouse.js +189 -21
- package/lib/common/src/menu/menuR.d.ts +3 -3
- package/lib/common/src/menu/menuR.js +5 -5
- package/lib/common/src/styles/styleGrid.d.ts +1 -1
- package/lib/common/src/styles/tokens.d.ts +1 -1
- package/lib/common/src/styles/tokens.js +1 -1
- package/lib/common/src/utils/cache.d.ts +11 -11
- package/lib/common/src/utils/cache.js +0 -0
- package/lib/common/src/utils/gridRows.d.ts +37 -0
- package/lib/common/src/utils/{applyTransactionAsyncUpdate.js → gridRows.js} +6 -38
- package/lib/common/src/utils/index.d.ts +2 -2
- package/lib/common/src/utils/index.js +2 -2
- package/lib/common/src/utils/inputAutoStep.d.ts +2 -2
- package/lib/common/src/utils/inputAutoStep.js +26 -37
- package/lib/common/src/utils/{mapMemory.d.ts → memoryStore.d.ts} +13 -13
- package/lib/common/src/utils/{mapMemory.js → memoryStore.js} +32 -32
- package/lib/common/src/utils/observableMap.d.ts +5 -5
- package/lib/common/src/utils/observableMap.js +3 -3
- package/lib/common/updateBy.js +2 -2
- package/lib/style/style.css +3 -3
- package/lib/style/tokens.css +1 -1
- package/package.json +49 -49
- package/lib/common/src/components/Dnd/DraggableOutlineDiv.d.ts +0 -2
- package/lib/common/src/hooks/useAddDownAnyKey.d.ts +0 -24
- package/lib/common/src/utils/applyTransactionAsyncUpdate.d.ts +0 -53
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
/** createToolbar - a customizable, self-describing toolbar primitive.
|
|
3
3
|
* Three decoupled layers: config (plain serializable data, persisted via
|
|
4
|
-
*
|
|
4
|
+
* memoryProps -> memoryCache, same mechanics as createUiSlot), Bar (renders visible
|
|
5
5
|
* items in config order at config density) and Settings (a pure editor that
|
|
6
6
|
* only reads/writes config) - so the SAME Settings element works both in the
|
|
7
7
|
* Bar's own gear popover and in a global settings section.
|
|
8
8
|
* v1 non-goals: no overflow/"more" menu (an overflow hook would live in Bar,
|
|
9
9
|
* right after the visible-items map), no grouping, no cross-bar drag. */
|
|
10
|
-
export type
|
|
10
|
+
export type ToolbarItem = {
|
|
11
11
|
/** stable id (persist key) */
|
|
12
12
|
key: string;
|
|
13
13
|
/** full human name - shown in the Settings list */
|
|
14
14
|
title: string;
|
|
15
15
|
/** short caption for 'label' density (falls back to title) */
|
|
16
16
|
short?: string;
|
|
17
|
-
/** compact glyph/icon for 'icon' density
|
|
18
|
-
|
|
17
|
+
/** compact glyph/icon for 'icon' density; absent = the first letters of
|
|
18
|
+
* short/title render as a text pseudo-icon */
|
|
19
|
+
icon?: React.ReactNode;
|
|
19
20
|
/** full custom render; default = icon [+ short] by the density registry */
|
|
20
21
|
render?: (density: string) => React.ReactNode;
|
|
21
22
|
/** convenience for plain action buttons; interactive items can also handle clicks inside render() */
|
|
@@ -25,7 +26,7 @@ export type tToolbarItem = {
|
|
|
25
26
|
/** cannot be hidden or reordered (pinned to its index in opts.items) */
|
|
26
27
|
fixed?: boolean;
|
|
27
28
|
};
|
|
28
|
-
export type
|
|
29
|
+
export type ToolbarConfig = {
|
|
29
30
|
/** item keys, display order */
|
|
30
31
|
order: string[];
|
|
31
32
|
visible: {
|
|
@@ -34,25 +35,51 @@ export type tToolbarConfig = {
|
|
|
34
35
|
/** a key from the density registry ('icon' | 'label' | registered extras) */
|
|
35
36
|
density: string;
|
|
36
37
|
};
|
|
37
|
-
|
|
38
|
+
/** The order/visibility slice of a config - what an external source owns. */
|
|
39
|
+
export type UiListConfig = {
|
|
40
|
+
order: string[];
|
|
41
|
+
visible: {
|
|
42
|
+
[key: string]: boolean;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
/** An external owner of order/visibility that a Toolbar can run on instead of
|
|
46
|
+
* its own store - e.g. columnState's listSource: the SAME config the grid
|
|
47
|
+
* syncs with, so dragging a column in the grid reorders the toolbar and
|
|
48
|
+
* vice versa. Density and the gear flag stay toolbar-local (persisted under
|
|
49
|
+
* the toolbar's own key). useConfig must be a React hook (subscription). */
|
|
50
|
+
export type UiListSource = {
|
|
51
|
+
useConfig: () => UiListConfig;
|
|
52
|
+
getConfig: () => UiListConfig;
|
|
53
|
+
setConfig: (next: UiListConfig) => void;
|
|
54
|
+
/** re-emitted as the toolbar's own onChange (grid-driven changes included) */
|
|
55
|
+
onChange?: (cb: (cfg: UiListConfig) => void) => () => void;
|
|
56
|
+
};
|
|
57
|
+
export type ToolbarDensity = {
|
|
38
58
|
key: string;
|
|
39
59
|
/** human name for the Settings segmented control */
|
|
40
60
|
name: string;
|
|
41
61
|
/** how one item renders at this density; absent = icon + (short ?? title) */
|
|
42
|
-
renderItem?: (item:
|
|
62
|
+
renderItem?: (item: ToolbarItem) => React.ReactNode;
|
|
43
63
|
};
|
|
64
|
+
/** Icon with a text fallback: no glyph = the first letters of short/title
|
|
65
|
+
* (an "PR"-style pseudo-icon), so icon densities work for icon-less items. */
|
|
66
|
+
export declare function toolbarItemIcon(item: {
|
|
67
|
+
icon?: React.ReactNode;
|
|
68
|
+
short?: string;
|
|
69
|
+
title: string;
|
|
70
|
+
}): React.ReactNode;
|
|
44
71
|
/** Register an extra density level. Re-register with the same key replaces the
|
|
45
72
|
* previous one. The returned function removes exactly this registration. */
|
|
46
|
-
export declare function registerToolbarDensity(d:
|
|
73
|
+
export declare function registerToolbarDensity(d: ToolbarDensity): () => void;
|
|
47
74
|
/** Current density registry (built-ins + registered). */
|
|
48
|
-
export declare function getToolbarDensities(): readonly
|
|
75
|
+
export declare function getToolbarDensities(): readonly ToolbarDensity[];
|
|
49
76
|
export declare function createToolbar(opts: {
|
|
50
|
-
/** persistence key (
|
|
77
|
+
/** persistence key (memoryProps -> memoryCache), like createUiSlot */
|
|
51
78
|
key: string;
|
|
52
79
|
/** item descriptors; the config only ever references them by key */
|
|
53
|
-
items:
|
|
80
|
+
items: ToolbarItem[];
|
|
54
81
|
/** defaults; missing fields are derived from items */
|
|
55
|
-
def?: Partial<
|
|
82
|
+
def?: Partial<ToolbarConfig>;
|
|
56
83
|
/** the gear button's face: icon/title default to a plain gear svg. The gear
|
|
57
84
|
* renders only when Bar mounts with settings, AND visible['__settings'] is
|
|
58
85
|
* not false - the Settings editor shows a separated toggle row for it. */
|
|
@@ -60,6 +87,12 @@ export declare function createToolbar(opts: {
|
|
|
60
87
|
title?: string;
|
|
61
88
|
icon?: React.ReactNode;
|
|
62
89
|
};
|
|
90
|
+
/** external owner of order/visibility (columnState.api.listSource etc.):
|
|
91
|
+
* the toolbar becomes a VIEW over that config - Bar/Settings edit it, and
|
|
92
|
+
* outside changes (a column dragged in the grid) reorder the toolbar.
|
|
93
|
+
* Item keys should match the source's keys 1:1. Density and the gear flag
|
|
94
|
+
* remain in the toolbar's own store under `key`. Default: own store. */
|
|
95
|
+
source?: UiListSource;
|
|
63
96
|
}): {
|
|
64
97
|
Bar: (p?: {
|
|
65
98
|
className?: string;
|
|
@@ -71,35 +104,15 @@ export declare function createToolbar(opts: {
|
|
|
71
104
|
activeClassName?: string;
|
|
72
105
|
}) => import("react/jsx-runtime").JSX.Element;
|
|
73
106
|
api: {
|
|
74
|
-
useConfig: () =>
|
|
107
|
+
useConfig: () => ToolbarConfig;
|
|
75
108
|
useItems: () => {
|
|
76
|
-
item:
|
|
109
|
+
item: ToolbarItem;
|
|
77
110
|
density: string;
|
|
78
111
|
content: string | number | bigint | boolean | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | import("react/jsx-runtime").JSX.Element | null | undefined;
|
|
79
112
|
}[];
|
|
80
|
-
getConfig: () =>
|
|
81
|
-
setConfig: (next:
|
|
113
|
+
getConfig: () => ToolbarConfig;
|
|
114
|
+
setConfig: (next: ToolbarConfig) => void;
|
|
82
115
|
reset: () => void;
|
|
83
|
-
onChange:
|
|
84
|
-
func: import("wenay-common2").Listener<[tToolbarConfig]>;
|
|
85
|
-
isRun: () => boolean;
|
|
86
|
-
run: () => void;
|
|
87
|
-
close: () => void;
|
|
88
|
-
eventClose: (cb: () => void) => () => void;
|
|
89
|
-
onClose: (cb: () => void) => () => void;
|
|
90
|
-
removeEventClose: (cb: () => void) => void;
|
|
91
|
-
on: import("wenay-common2").ListenOn<[tToolbarConfig]>;
|
|
92
|
-
off: (k: import("wenay-common2").Listener<[tToolbarConfig]> | (string | symbol) | null) => void;
|
|
93
|
-
addListen: (cb: import("wenay-common2").Listener<[tToolbarConfig]>, opts?: {
|
|
94
|
-
cbClose?: () => void;
|
|
95
|
-
key?: string | symbol;
|
|
96
|
-
}) => () => void;
|
|
97
|
-
removeListen: (k: import("wenay-common2").Listener<[tToolbarConfig]> | (string | symbol) | null) => void;
|
|
98
|
-
once: (cb: import("wenay-common2").Listener<[tToolbarConfig]>, opts?: {
|
|
99
|
-
key?: string | symbol;
|
|
100
|
-
}) => () => void;
|
|
101
|
-
count: () => number;
|
|
102
|
-
readonly getAllKeys: (string | symbol)[];
|
|
103
|
-
};
|
|
116
|
+
onChange: import("wenay-common2").ListenApi<[ToolbarConfig]>;
|
|
104
117
|
};
|
|
105
118
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { listen as createListen } from 'wenay-common2';
|
|
4
4
|
import { renderBy, updateBy } from '../../../updateBy';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { memoryGetOrCreate, memoryMarkDirty } from '../../utils/memoryStore';
|
|
6
|
+
import { OutsideClickArea } from '../../hooks/useOutside';
|
|
7
7
|
import { useReorder } from '../../hooks/useReorder';
|
|
8
8
|
// Module singleton (closure + updateBy subscription, no React context) - same
|
|
9
9
|
// pattern as the settings-section registry. Two built-in levels; a third
|
|
@@ -12,8 +12,13 @@ const densities = { list: [
|
|
|
12
12
|
{ key: 'icon', name: 'Icons', renderItem: itemIconOnly },
|
|
13
13
|
{ key: 'label', name: 'Icons + labels' },
|
|
14
14
|
] };
|
|
15
|
+
/** Icon with a text fallback: no glyph = the first letters of short/title
|
|
16
|
+
* (an "PR"-style pseudo-icon), so icon densities work for icon-less items. */
|
|
17
|
+
export function toolbarItemIcon(item) {
|
|
18
|
+
return item.icon ?? _jsx("span", { style: { fontSize: 10, fontWeight: 700, letterSpacing: 0.5, textTransform: 'uppercase' }, children: (item.short ?? item.title).slice(0, 3) });
|
|
19
|
+
}
|
|
15
20
|
function itemIconOnly(item) {
|
|
16
|
-
return item
|
|
21
|
+
return toolbarItemIcon(item);
|
|
17
22
|
}
|
|
18
23
|
/** Register an extra density level. Re-register with the same key replaces the
|
|
19
24
|
* previous one. The returned function removes exactly this registration. */
|
|
@@ -42,7 +47,7 @@ function itemContent(item, densityKey) {
|
|
|
42
47
|
const d = densities.list.find(e => e.key == densityKey);
|
|
43
48
|
if (d?.renderItem)
|
|
44
49
|
return d.renderItem(item);
|
|
45
|
-
return _jsxs(_Fragment, { children: [_jsx("span", { className: 'wenayTbIcon', children: item.icon }), _jsx("span", { className: 'wenayTbLabel', children: item.short ?? item.title })] });
|
|
50
|
+
return _jsxs(_Fragment, { children: [item.icon != null && _jsx("span", { className: 'wenayTbIcon', children: item.icon }), _jsx("span", { className: 'wenayTbLabel', children: item.short ?? item.title })] });
|
|
46
51
|
}
|
|
47
52
|
/** Reserved visible-map key for the bar's settings (gear) button: not part of
|
|
48
53
|
* order (the gear always sits at the bar edge), but toggleable like an item. */
|
|
@@ -53,15 +58,24 @@ export function createToolbar(opts) {
|
|
|
53
58
|
visible: opts.def?.visible ? { ...opts.def.visible } : Object.fromEntries(opts.items.map(i => [i.key, i.defaultVisible != false])),
|
|
54
59
|
density: opts.def?.density ?? densities.list[0].key,
|
|
55
60
|
});
|
|
56
|
-
const st =
|
|
57
|
-
const [emitChange, onChange] =
|
|
61
|
+
const st = memoryGetOrCreate(opts.key, defConfig());
|
|
62
|
+
const [emitChange, onChange] = createListen();
|
|
63
|
+
// ext is fixed for the controller's lifetime, so hook call order inside
|
|
64
|
+
// useConfig/Bar/Settings never changes for a given toolbar instance
|
|
65
|
+
const ext = opts.source;
|
|
66
|
+
// outside edits of the external config (grid drags...) must reach the
|
|
67
|
+
// toolbar's own subscribers too; module-lifetime subscription by design
|
|
68
|
+
ext?.onChange?.(() => emitChange(normalize()));
|
|
69
|
+
/** raw order/visible as stored - own store or the external source */
|
|
70
|
+
const rawList = () => ext ? ext.getConfig() : { order: st.order, visible: st.visible };
|
|
58
71
|
/** The persisted state may be stale or partial (older app version, removed
|
|
59
72
|
* items, an unregistered density) - never crash, never drop user data that
|
|
60
73
|
* still applies: unknown keys are filtered out, missing items are appended
|
|
61
74
|
* (default-visible), fixed items are pinned back to their descriptor index. */
|
|
62
75
|
function normalize() {
|
|
76
|
+
const raw = rawList();
|
|
63
77
|
const known = new Set(opts.items.map(i => i.key));
|
|
64
|
-
const rawOrder = Array.isArray(
|
|
78
|
+
const rawOrder = Array.isArray(raw.order) ? raw.order : [];
|
|
65
79
|
const order = rawOrder.filter(k => known.has(k) && !opts.items.find(i => i.key == k)?.fixed);
|
|
66
80
|
for (const it of opts.items)
|
|
67
81
|
if (!it.fixed && order.indexOf(it.key) == -1)
|
|
@@ -70,29 +84,52 @@ export function createToolbar(opts) {
|
|
|
70
84
|
if (it.fixed)
|
|
71
85
|
order.splice(Math.min(i, order.length), 0, it.key);
|
|
72
86
|
});
|
|
73
|
-
const rawVisible =
|
|
87
|
+
const rawVisible = raw.visible && typeof raw.visible == 'object' ? raw.visible : {};
|
|
74
88
|
const visible = {};
|
|
75
89
|
for (const it of opts.items)
|
|
76
90
|
visible[it.key] = it.fixed ? true : (rawVisible[it.key] ?? it.defaultVisible != false);
|
|
77
|
-
|
|
91
|
+
// the gear flag is toolbar-local: an external source only owns items
|
|
92
|
+
const gearRaw = (ext ? st.visible : rawVisible) ?? {};
|
|
93
|
+
visible[SETTINGS_KEY] = typeof gearRaw[SETTINGS_KEY] == 'boolean' ? gearRaw[SETTINGS_KEY] : true;
|
|
78
94
|
const density = typeof st.density == 'string' && densities.list.some(d => d.key == st.density)
|
|
79
95
|
? st.density : (opts.def?.density ?? densities.list[0].key);
|
|
80
96
|
return { order, visible, density };
|
|
81
97
|
}
|
|
82
98
|
/** Every edit funnels through here: mutate the persisted object in place
|
|
83
99
|
* (identity is the updateBy/renderBy subscription key), announce, mark the
|
|
84
|
-
* cache dirty, emit outward.
|
|
100
|
+
* cache dirty, emit outward. With an external source the items' order and
|
|
101
|
+
* visibility go THERE (its own change flow re-emits); density + gear flag
|
|
102
|
+
* always stay in the toolbar's own store. */
|
|
85
103
|
function setConfig(next) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
104
|
+
if (ext) {
|
|
105
|
+
const visible = { ...next.visible };
|
|
106
|
+
delete visible[SETTINGS_KEY];
|
|
107
|
+
st.visible = { ...st.visible, [SETTINGS_KEY]: next.visible[SETTINGS_KEY] != false };
|
|
108
|
+
st.density = next.density;
|
|
109
|
+
renderBy(st);
|
|
110
|
+
memoryMarkDirty(opts.key);
|
|
111
|
+
ext.setConfig({ order: next.order.slice(), visible });
|
|
112
|
+
// the source's own onChange already re-emitted; emit only when it can't
|
|
113
|
+
if (!ext.onChange)
|
|
114
|
+
emitChange(normalize());
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
st.order = next.order.slice();
|
|
118
|
+
st.visible = { ...next.visible };
|
|
119
|
+
st.density = next.density;
|
|
120
|
+
renderBy(st);
|
|
121
|
+
memoryMarkDirty(opts.key);
|
|
122
|
+
emitChange(normalize());
|
|
123
|
+
}
|
|
92
124
|
}
|
|
93
125
|
const getConfig = () => normalize();
|
|
94
|
-
|
|
126
|
+
/** subscription to everything the toolbar renders from (hook) */
|
|
127
|
+
function useSubscribe() {
|
|
95
128
|
updateBy(st);
|
|
129
|
+
ext?.useConfig(); // ext is per-instance constant - hook order is stable
|
|
130
|
+
}
|
|
131
|
+
function useConfig() {
|
|
132
|
+
useSubscribe();
|
|
96
133
|
return normalize();
|
|
97
134
|
}
|
|
98
135
|
/** Headless bar: the ordered, visibility-filtered items plus their rendered
|
|
@@ -102,7 +139,7 @@ export function createToolbar(opts) {
|
|
|
102
139
|
* consumer re-renders from this list rather than the library re-parenting
|
|
103
140
|
* someone else's nodes.) */
|
|
104
141
|
function useItems() {
|
|
105
|
-
|
|
142
|
+
useSubscribe();
|
|
106
143
|
updateBy(densities);
|
|
107
144
|
const cfg = normalize();
|
|
108
145
|
const byKey = new Map(opts.items.map(i => [i.key, i]));
|
|
@@ -132,7 +169,7 @@ export function createToolbar(opts) {
|
|
|
132
169
|
* global settings menu. popAlign: which bar edge the popover sticks to -
|
|
133
170
|
* 'right' (default, for bars in a top-right corner) or 'left'. */
|
|
134
171
|
function Bar(p = {}) {
|
|
135
|
-
|
|
172
|
+
useSubscribe();
|
|
136
173
|
updateBy(densities);
|
|
137
174
|
const cfg = normalize();
|
|
138
175
|
const [open, setOpen] = useState(false);
|
|
@@ -142,7 +179,7 @@ export function createToolbar(opts) {
|
|
|
142
179
|
if (!it || cfg.visible[k] == false)
|
|
143
180
|
return null;
|
|
144
181
|
return _jsx("div", { className: 'wenayTbItem', title: cfg.density == 'icon' ? it.title : undefined, onClick: it.onClick, children: itemContent(it, cfg.density) }, k);
|
|
145
|
-
}), p.settings && cfg.visible[SETTINGS_KEY] != false && _jsxs(
|
|
182
|
+
}), p.settings && cfg.visible[SETTINGS_KEY] != false && _jsxs(OutsideClickArea, { className: 'wenayTbGear', status: open, outsideClick: () => setOpen(false), children: [_jsx("div", { className: 'wenayTbItem', title: settingsTitle(), onClick: () => setOpen(v => !v), children: settingsIcon() }), open && _jsx("div", { className: p.popAlign == 'left' ? 'wenayTbPop wenayTbPopLeft' : 'wenayTbPop', children: _jsx(Settings, {}) })] })] });
|
|
146
183
|
}
|
|
147
184
|
/** The pure editor over config: density segments + one row per item
|
|
148
185
|
* (visibility checkbox, icon preview, title, drag handle). Reorder rides the
|
|
@@ -152,7 +189,7 @@ export function createToolbar(opts) {
|
|
|
152
189
|
* never move in the preview and a drop never lands elsewhere than shown.
|
|
153
190
|
* Plus arrow keys on the focused handle; fixed rows have neither. */
|
|
154
191
|
function Settings(p = {}) {
|
|
155
|
-
|
|
192
|
+
useSubscribe();
|
|
156
193
|
updateBy(densities);
|
|
157
194
|
const cfg = normalize();
|
|
158
195
|
const base = p.className ?? 'wenaySegBtn';
|
|
@@ -199,7 +236,7 @@ export function createToolbar(opts) {
|
|
|
199
236
|
if (!e.target.closest('input'))
|
|
200
237
|
e.currentTarget.querySelector('.wenayTbHandle')?.focus();
|
|
201
238
|
r.props.onMouseDown(e);
|
|
202
|
-
}, 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: it
|
|
239
|
+
}, 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);
|
|
203
240
|
}) }), _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() })] })] });
|
|
204
241
|
}
|
|
205
242
|
return {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
/** One UI block shown in exactly one of several mount points; the point is a persisted setting.
|
|
3
|
-
* Persistence rides the existing
|
|
4
|
-
* setPlace announces its in-place mutation (
|
|
5
|
-
* via
|
|
3
|
+
* Persistence rides the existing memoryProps -> memoryCache mechanics: memoryProps is observable,
|
|
4
|
+
* setPlace announces its in-place mutation (memoryMarkDirty), the app decides when to save
|
|
5
|
+
* via memoryCache.onDirty - same as window state (floatingWindowMap etc.).
|
|
6
6
|
* Mount points render <Slot place="..."> themselves and decide nothing: the slot compares
|
|
7
7
|
* the persisted place with its own and renders children only on a match. */
|
|
8
8
|
export declare function createUiSlot<Places extends string>(opts: {
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { renderBy, updateBy } from "../../../updateBy";
|
|
3
|
-
import {
|
|
3
|
+
import { memoryGetOrCreate, memoryMarkDirty } from "../../utils/memoryStore";
|
|
4
4
|
/** One UI block shown in exactly one of several mount points; the point is a persisted setting.
|
|
5
|
-
* Persistence rides the existing
|
|
6
|
-
* setPlace announces its in-place mutation (
|
|
7
|
-
* via
|
|
5
|
+
* Persistence rides the existing memoryProps -> memoryCache mechanics: memoryProps is observable,
|
|
6
|
+
* setPlace announces its in-place mutation (memoryMarkDirty), the app decides when to save
|
|
7
|
+
* via memoryCache.onDirty - same as window state (floatingWindowMap etc.).
|
|
8
8
|
* Mount points render <Slot place="..."> themselves and decide nothing: the slot compares
|
|
9
9
|
* the persisted place with its own and renders children only on a match. */
|
|
10
10
|
export function createUiSlot(opts) {
|
|
11
|
-
const st =
|
|
11
|
+
const st = memoryGetOrCreate(opts.key, { place: opts.def });
|
|
12
12
|
// a stored place may no longer exist after an app update - fall back to def
|
|
13
13
|
const isPlace = (p) => typeof p == "string" && p in opts.places;
|
|
14
14
|
const getPlace = () => isPlace(st.place) ? st.place : opts.def;
|
|
@@ -17,7 +17,7 @@ export function createUiSlot(opts) {
|
|
|
17
17
|
return;
|
|
18
18
|
st.place = p;
|
|
19
19
|
renderBy(st);
|
|
20
|
-
|
|
20
|
+
memoryMarkDirty(opts.key);
|
|
21
21
|
};
|
|
22
22
|
function Slot(p) {
|
|
23
23
|
updateBy(st);
|
|
@@ -20,7 +20,7 @@ export type UseAgGridOptions<T> = {
|
|
|
20
20
|
*
|
|
21
21
|
* const grid = useAgGrid<Row>({ getId })
|
|
22
22
|
* const grid = useAgGrid({ core: mainTable })
|
|
23
|
-
* <
|
|
23
|
+
* <AgGridTable<Row> controller={grid} columnDefs={cols} />
|
|
24
24
|
* grid.update({ newData })
|
|
25
25
|
*/
|
|
26
26
|
export declare function useAgGrid<T>(options?: UseAgGridOptions<T>): {
|
|
@@ -50,7 +50,7 @@ export declare function useAgGrid<T>(options?: UseAgGridOptions<T>): {
|
|
|
50
50
|
flushAsyncTransactions: () => boolean;
|
|
51
51
|
};
|
|
52
52
|
export type AgGridController<T> = ReturnType<typeof useAgGrid<T>>;
|
|
53
|
-
export type
|
|
53
|
+
export type AgGridTableProps<T> = AgGridReactProps<T> & {
|
|
54
54
|
/** Controller from useAgGrid: all wiring (getRowId, ready/destroy, sync) is inside. */
|
|
55
55
|
controller?: AgGridController<T>;
|
|
56
56
|
/** Declarative row upsert for simple cases without a controller. */
|
|
@@ -58,6 +58,6 @@ export type AgGridMyProps<T> = AgGridReactProps<T> & {
|
|
|
58
58
|
/** Auto-fit columns to the container. Defaults to true. */
|
|
59
59
|
autoSizeColumns?: boolean;
|
|
60
60
|
};
|
|
61
|
-
declare function
|
|
62
|
-
export declare const
|
|
61
|
+
declare function AgGridTableInner<T>(props: AgGridTableProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
62
|
+
export declare const AgGridTable: typeof AgGridTableInner;
|
|
63
63
|
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
// Layer 2 (React): headless hook over the core + custom <
|
|
3
|
-
// The hook does NOT render; it returns a controller.
|
|
2
|
+
// Layer 2 (React): headless hook over the core + custom <AgGridTable> component.
|
|
3
|
+
// The hook does NOT render; it returns a controller. AgGridTable is our AgGridReact with
|
|
4
4
|
// project defaults (theme, memo, auto sizing, selection, lifecycle). Wiring is the
|
|
5
5
|
// controller prop; bare <AgGridReact> can still spread gridProps (as in agGrid3).
|
|
6
6
|
import { memo, useEffect, useMemo, useRef, useState } from 'react';
|
|
@@ -24,7 +24,7 @@ function ensureAgGridModules() {
|
|
|
24
24
|
*
|
|
25
25
|
* const grid = useAgGrid<Row>({ getId })
|
|
26
26
|
* const grid = useAgGrid({ core: mainTable })
|
|
27
|
-
* <
|
|
27
|
+
* <AgGridTable<Row> controller={grid} columnDefs={cols} />
|
|
28
28
|
* grid.update({ newData })
|
|
29
29
|
*/
|
|
30
30
|
export function useAgGrid(options) {
|
|
@@ -97,11 +97,11 @@ export function useAgGrid(options) {
|
|
|
97
97
|
};
|
|
98
98
|
}, [core, gridProps]);
|
|
99
99
|
}
|
|
100
|
-
// ---
|
|
100
|
+
// --- AgGridTable: our grid with project defaults --------------------------------
|
|
101
101
|
// v35: the canonical row-selection form is an object, not the legacy "multiple" string.
|
|
102
102
|
// Stable module-level reference so the grid does not re-evaluate the option on each render.
|
|
103
103
|
const ROW_SELECTION = { mode: 'multiRow' };
|
|
104
|
-
function
|
|
104
|
+
function AgGridTableInner(props) {
|
|
105
105
|
const { controller, data, autoSizeColumns = true, defaultColDef, theme, getRowId, onGridReady, onGridPreDestroyed, ...rest } = props;
|
|
106
106
|
// Own controller is overlay-safe: rowData grids without a controller must not be cleared on attach.
|
|
107
107
|
const own = useAgGrid({
|
|
@@ -152,4 +152,4 @@ function AgGridMyInner(props) {
|
|
|
152
152
|
}
|
|
153
153
|
// memo: the grid does not re-render from unrelated page state; data updates bypass
|
|
154
154
|
// render (controller.updateData). The cast is required because memo() erases the component generic.
|
|
155
|
-
export const
|
|
155
|
+
export const AgGridTable = memo(AgGridTableInner);
|
|
@@ -2,9 +2,9 @@ export { createGridBuffer } from './core';
|
|
|
2
2
|
export type { BufferTable, CleanArgs, CreateGridBufferOptions, GetId, GridApiLike, GridBufferCore, GridBufferMode, GridTransaction, PushOptions, RowId, UpdateArgs, } from './core';
|
|
3
3
|
export { createColumnBuffer } from './columnBuffer';
|
|
4
4
|
export type { ColumnApplyContext, ColumnAttach, ColumnBuffer } from './columnBuffer';
|
|
5
|
-
export { useAgGrid,
|
|
6
|
-
export type { AgGridController,
|
|
5
|
+
export { useAgGrid, AgGridTable } from './agGrid4';
|
|
6
|
+
export type { AgGridController, AgGridTableProps, UseAgGridOptions } from './agGrid4';
|
|
7
7
|
export { numericComparator, colDefCentered, colDefWrap } from './gridUtils';
|
|
8
8
|
export type { NumberPair } from './gridUtils';
|
|
9
9
|
export { useAgGridTheme, buildAgTheme } from './theme';
|
|
10
|
-
export type {
|
|
10
|
+
export type { ThemeMode } from './theme';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { createGridBuffer } from './core';
|
|
2
2
|
export { createColumnBuffer } from './columnBuffer';
|
|
3
|
-
export { useAgGrid,
|
|
3
|
+
export { useAgGrid, AgGridTable } from './agGrid4';
|
|
4
4
|
export { numericComparator, colDefCentered, colDefWrap } from './gridUtils';
|
|
5
5
|
export { useAgGridTheme, buildAgTheme } from './theme';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export type
|
|
1
|
+
export type ThemeMode = 'light' | 'dark';
|
|
2
2
|
/** Pure ag-grid theme builder from mode. No React. Cached per mode: one shared
|
|
3
3
|
* theme object across all grids instead of one per component instance.
|
|
4
4
|
* dark uses the same parts and params as legacy GridStyleDefault for a consistent grid look. */
|
|
5
|
-
export declare function buildAgTheme(mode:
|
|
5
|
+
export declare function buildAgTheme(mode: ThemeMode): import("ag-grid-community").Theme<import("ag-grid-community").ThemeDefaultParams>;
|
|
6
6
|
/** Application theme; default is dark, as in production. */
|
|
7
|
-
export declare function useAgGridTheme(mode?:
|
|
7
|
+
export declare function useAgGridTheme(mode?: ThemeMode): import("ag-grid-community").Theme<import("ag-grid-community").ThemeDefaultParams>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ColumnStateController } from './columnState';
|
|
3
|
+
export declare function CardList<T extends object>(p: {
|
|
4
|
+
state: ColumnStateController;
|
|
5
|
+
data: readonly T[];
|
|
6
|
+
getId?: (row: T, index: number) => string;
|
|
7
|
+
/** custom field renderer; default = String(row[key]) */
|
|
8
|
+
renderValue?: (key: string, row: T) => React.ReactNode;
|
|
9
|
+
className?: string;
|
|
10
|
+
style?: React.CSSProperties;
|
|
11
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
function cmpValues(a, b) {
|
|
3
|
+
if (typeof a == 'number' && typeof b == 'number')
|
|
4
|
+
return a - b;
|
|
5
|
+
if (a == null && b == null)
|
|
6
|
+
return 0;
|
|
7
|
+
if (a == null)
|
|
8
|
+
return -1;
|
|
9
|
+
if (b == null)
|
|
10
|
+
return 1;
|
|
11
|
+
return String(a).localeCompare(String(b));
|
|
12
|
+
}
|
|
13
|
+
export function CardList(p) {
|
|
14
|
+
const cfg = p.state.api.useConfig();
|
|
15
|
+
const cols = p.state.columns;
|
|
16
|
+
const byKey = new Map(cols.map(c => [c.key, c]));
|
|
17
|
+
const keys = p.state.api.visibleKeys();
|
|
18
|
+
const titleKey = cols.find(c => c.cardRole == 'title' && cfg.visible[c.key] != false)?.key ?? keys[0];
|
|
19
|
+
const accentKey = cols.find(c => c.cardRole == 'accent' && cfg.visible[c.key] != false)?.key;
|
|
20
|
+
const fieldKeys = keys.filter(k => k != titleKey && k != accentKey);
|
|
21
|
+
const value = (key, row) => p.renderValue?.(key, row) ?? String(row[key] ?? '');
|
|
22
|
+
const rows = [...p.data];
|
|
23
|
+
if (cfg.sort) { // sticky sort: applies even when its column is hidden
|
|
24
|
+
const { key, dir } = cfg.sort;
|
|
25
|
+
rows.sort((a, b) => cmpValues(a[key], b[key]) * (dir == 'asc' ? 1 : -1));
|
|
26
|
+
}
|
|
27
|
+
return _jsx("div", { className: p.className, style: { display: 'grid', gap: 8, ...p.style }, children: rows.map((row, i) => (_jsxs("div", { style: { border: '1px solid #d0d7de', borderRadius: 8, padding: '8px 10px', background: '#fff' }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: fieldKeys.length ? 6 : 0 }, children: [_jsx("b", { style: { fontSize: 14, flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, children: titleKey ? value(titleKey, row) : '' }), accentKey && _jsx("span", { style: { fontSize: 11, padding: '1px 8px', borderRadius: 10, background: '#ddf4ff', color: '#0969da', whiteSpace: 'nowrap' }, children: value(accentKey, row) })] }), fieldKeys.map(k => (_jsxs("div", { style: { display: 'flex', gap: 10, fontSize: 12, lineHeight: 1.7 }, children: [_jsx("span", { style: { color: '#57606a', minWidth: 72 }, children: byKey.get(k)?.title ?? k }), _jsx("span", { style: { flex: 1, textAlign: 'right', overflow: 'hidden', textOverflow: 'ellipsis' }, children: value(k, row) })] }, k)))] }, p.getId?.(row, i) ?? i))) });
|
|
28
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ColumnStateController } from './columnState';
|
|
3
|
+
export declare function ColumnDots(p: {
|
|
4
|
+
state: ColumnStateController;
|
|
5
|
+
/** max simultaneous dots (default 4) */
|
|
6
|
+
max?: number;
|
|
7
|
+
className?: string;
|
|
8
|
+
style?: React.CSSProperties;
|
|
9
|
+
}): import("react/jsx-runtime").JSX.Element;
|