@djangocfg/ui-tools 2.1.416 → 2.1.417
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/dist/audio-player/index.cjs +2099 -0
- package/dist/audio-player/index.cjs.map +1 -0
- package/dist/audio-player/index.css +65 -0
- package/dist/audio-player/index.css.map +1 -0
- package/dist/audio-player/index.d.cts +174 -0
- package/dist/audio-player/index.d.ts +174 -0
- package/dist/audio-player/index.mjs +2076 -0
- package/dist/audio-player/index.mjs.map +1 -0
- package/dist/composer-registry/index.cjs +45 -0
- package/dist/composer-registry/index.cjs.map +1 -0
- package/dist/composer-registry/index.d.cts +73 -0
- package/dist/composer-registry/index.d.ts +73 -0
- package/dist/composer-registry/index.mjs +39 -0
- package/dist/composer-registry/index.mjs.map +1 -0
- package/dist/tree/index.cjs +85 -63
- package/dist/tree/index.cjs.map +1 -1
- package/dist/tree/index.d.cts +15 -1
- package/dist/tree/index.d.ts +15 -1
- package/dist/tree/index.mjs +86 -64
- package/dist/tree/index.mjs.map +1 -1
- package/package.json +14 -9
- package/src/tools/chat/composer/Composer.tsx +8 -8
- package/src/tools/chat/context/ChatProvider.tsx +13 -78
- package/src/tools/chat/hooks/useAutoFocusOnStreamEnd.ts +12 -15
- package/src/tools/chat/hooks/useFocusOnEmptyClick.ts +4 -5
- package/src/tools/chat/launcher/header/ChatHeader.tsx +14 -19
- package/src/tools/chat/launcher/header/ChatHeaderActionButton.tsx +8 -12
- package/src/tools/data/Tree/TreeRoot.tsx +33 -109
- package/src/tools/data/Tree/components/TreeRow.tsx +11 -0
- package/src/tools/data/Tree/context/TreeContext.tsx +22 -3
- package/src/tools/data/Tree/context/menu/index.ts +1 -0
- package/src/tools/data/Tree/context/menu/render.tsx +75 -0
- package/src/tools/data/Tree/context/menu/use-resolved-menu.ts +16 -2
- package/src/tools/data/Tree/index.tsx +1 -0
- package/src/tools/data/Tree/types/index.ts +1 -1
- package/src/tools/data/Tree/types/root-props.ts +16 -0
- package/src/tools/dev/OpenapiViewer/components/DocsLayout/EndpointDoc/Header/MetaActions.tsx +6 -9
- package/src/tools/dev/OpenapiViewer/components/DocsLayout/index.tsx +2 -4
- package/src/tools/input/SpeechRecognition/widgets/VoiceComposerSlot.tsx +11 -12
- package/src/tools/integration/ComposerRegistry/index.ts +105 -0
- package/src/tools/media/AudioPlayer/Player.tsx +2 -0
- package/src/tools/media/AudioPlayer/PlayerShell.tsx +37 -22
- package/src/tools/media/AudioPlayer/lazy.tsx +30 -42
- package/src/tools/media/AudioPlayer/parts/Controls/IconButton.tsx +10 -11
- package/src/tools/media/AudioPlayer/parts/Controls/VolumeControl.tsx +52 -115
- package/src/tools/media/AudioPlayer/types.ts +15 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal imperative handle every text-editor surface implements so
|
|
3
|
+
* an external tool (voice dictation, command palette, AI suggestion)
|
|
4
|
+
* can read/write its text content without traversing React.
|
|
5
|
+
*
|
|
6
|
+
* Methods are optional so a host can register a partial handle
|
|
7
|
+
* (e.g. only `getValue` + `setValue`), and the caller checks before use.
|
|
8
|
+
*/
|
|
9
|
+
interface ComposerHandle {
|
|
10
|
+
/** Move keyboard focus into the composer's editable surface. */
|
|
11
|
+
focus: () => void;
|
|
12
|
+
/** Move the caret to the very end of the input. */
|
|
13
|
+
moveCursorToEnd?: () => void;
|
|
14
|
+
/** Read the current draft text. Voice dictation anchors partial
|
|
15
|
+
* transcripts onto the user's already-typed prefix via this. */
|
|
16
|
+
getValue?: () => string;
|
|
17
|
+
/** Replace the current draft text. Voice dictation pushes interim
|
|
18
|
+
* and final transcripts through this without owning a controlled
|
|
19
|
+
* binding. */
|
|
20
|
+
setValue?: (value: string) => void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* `@djangocfg/ui-tools/composer-registry`
|
|
24
|
+
*
|
|
25
|
+
* Cross-tool bridge: the currently-active text composer's handle.
|
|
26
|
+
*
|
|
27
|
+
* Producer side (`@djangocfg/ui-tools/chat` and TipTap hosts):
|
|
28
|
+
* register their composer's imperative handle via `attachComposer`.
|
|
29
|
+
*
|
|
30
|
+
* Consumer side (`@djangocfg/ui-tools/speech-recognition`):
|
|
31
|
+
* reads the active handle via `useActiveComposer`/`getActiveComposer`
|
|
32
|
+
* and pipes voice transcripts into it.
|
|
33
|
+
*
|
|
34
|
+
* Why this lives in its own subpath (not inside `chat`)
|
|
35
|
+
* ----------------------------------------------------
|
|
36
|
+
* `chat` and `speech-recognition` are sibling subpath exports. If the
|
|
37
|
+
* registry lived inside `chat`, then `speech-recognition` would have
|
|
38
|
+
* to reach into it via a cross-tool relative import — and under Vite
|
|
39
|
+
* dev's dependency optimizer that file ends up loaded TWICE (once via
|
|
40
|
+
* the `./chat` URL, once via the `./speech-recognition` relative-up
|
|
41
|
+
* URL), giving the producer and the consumer two separate `let active`
|
|
42
|
+
* slots. The active handle registered by chat would be invisible to
|
|
43
|
+
* speech-recognition (and vice versa).
|
|
44
|
+
*
|
|
45
|
+
* Putting the registry in its own dedicated subpath (a single tool
|
|
46
|
+
* that NEITHER chat nor speech-recognition cross-import — they both
|
|
47
|
+
* import this one as their dependency) means Vite resolves it from a
|
|
48
|
+
* single URL across the whole graph. One module instance, one shared
|
|
49
|
+
* `active` slot.
|
|
50
|
+
*
|
|
51
|
+
* Semantics: one active composer per realm. The most recent
|
|
52
|
+
* `registerComposer(handle)` wins; `registerComposer(null)` clears it.
|
|
53
|
+
*/
|
|
54
|
+
type Listener = (handle: ComposerHandle | null) => void;
|
|
55
|
+
/** Set or replace the active composer handle. Pass `null` to clear. */
|
|
56
|
+
declare function registerComposer(handle: ComposerHandle | null): void;
|
|
57
|
+
/**
|
|
58
|
+
* Convenience for components: register on mount, unregister on
|
|
59
|
+
* unmount. Returns a cleanup function suitable for `useEffect`.
|
|
60
|
+
*/
|
|
61
|
+
declare function attachComposer(handle: ComposerHandle): () => void;
|
|
62
|
+
/** Read the current active handle (no subscription). */
|
|
63
|
+
declare function getActiveComposer(): ComposerHandle | null;
|
|
64
|
+
/** Subscribe to handle changes; returns an unsubscribe fn. */
|
|
65
|
+
declare function subscribeComposer(listener: Listener): () => void;
|
|
66
|
+
/**
|
|
67
|
+
* React hook: re-renders the caller whenever the active composer
|
|
68
|
+
* changes. Built on `useSyncExternalStore` so concurrent rendering,
|
|
69
|
+
* SSR, and dev-mode strict-effects all behave correctly.
|
|
70
|
+
*/
|
|
71
|
+
declare function useActiveComposer(): ComposerHandle | null;
|
|
72
|
+
|
|
73
|
+
export { type ComposerHandle, attachComposer, getActiveComposer, registerComposer, subscribeComposer, useActiveComposer };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { __name } from '../chunk-PAWJFY3S.mjs';
|
|
2
|
+
import { useCallback, useSyncExternalStore } from 'react';
|
|
3
|
+
|
|
4
|
+
var active = null;
|
|
5
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
6
|
+
function registerComposer(handle) {
|
|
7
|
+
active = handle;
|
|
8
|
+
for (const fn of listeners) fn(active);
|
|
9
|
+
}
|
|
10
|
+
__name(registerComposer, "registerComposer");
|
|
11
|
+
function attachComposer(handle) {
|
|
12
|
+
registerComposer(handle);
|
|
13
|
+
return () => {
|
|
14
|
+
if (active === handle) registerComposer(null);
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
__name(attachComposer, "attachComposer");
|
|
18
|
+
function getActiveComposer() {
|
|
19
|
+
return active;
|
|
20
|
+
}
|
|
21
|
+
__name(getActiveComposer, "getActiveComposer");
|
|
22
|
+
function subscribeComposer(listener) {
|
|
23
|
+
listeners.add(listener);
|
|
24
|
+
return () => {
|
|
25
|
+
listeners.delete(listener);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
__name(subscribeComposer, "subscribeComposer");
|
|
29
|
+
function useActiveComposer() {
|
|
30
|
+
const subscribe = useCallback((onChange) => {
|
|
31
|
+
return subscribeComposer(onChange);
|
|
32
|
+
}, []);
|
|
33
|
+
return useSyncExternalStore(subscribe, getActiveComposer, () => null);
|
|
34
|
+
}
|
|
35
|
+
__name(useActiveComposer, "useActiveComposer");
|
|
36
|
+
|
|
37
|
+
export { attachComposer, getActiveComposer, registerComposer, subscribeComposer, useActiveComposer };
|
|
38
|
+
//# sourceMappingURL=index.mjs.map
|
|
39
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/tools/integration/ComposerRegistry/index.ts"],"names":[],"mappings":";;;AA6DA,IAAI,MAAA,GAAgC,IAAA;AACpC,IAAM,SAAA,uBAAgB,GAAA,EAAc;AAG7B,SAAS,iBAAiB,MAAA,EAAqC;AACpE,EAAA,MAAA,GAAS,MAAA;AACT,EAAA,KAAA,MAAW,EAAA,IAAM,SAAA,EAAW,EAAA,CAAG,MAAM,CAAA;AACvC;AAHgB,MAAA,CAAA,gBAAA,EAAA,kBAAA,CAAA;AAST,SAAS,eAAe,MAAA,EAAoC;AACjE,EAAA,gBAAA,CAAiB,MAAM,CAAA;AACvB,EAAA,OAAO,MAAM;AACX,IAAA,IAAI,MAAA,KAAW,MAAA,EAAQ,gBAAA,CAAiB,IAAI,CAAA;AAAA,EAC9C,CAAA;AACF;AALgB,MAAA,CAAA,cAAA,EAAA,gBAAA,CAAA;AAQT,SAAS,iBAAA,GAA2C;AACzD,EAAA,OAAO,MAAA;AACT;AAFgB,MAAA,CAAA,iBAAA,EAAA,mBAAA,CAAA;AAKT,SAAS,kBAAkB,QAAA,EAAgC;AAChE,EAAA,SAAA,CAAU,IAAI,QAAQ,CAAA;AACtB,EAAA,OAAO,MAAM;AACX,IAAA,SAAA,CAAU,OAAO,QAAQ,CAAA;AAAA,EAC3B,CAAA;AACF;AALgB,MAAA,CAAA,iBAAA,EAAA,mBAAA,CAAA;AAYT,SAAS,iBAAA,GAA2C;AACzD,EAAA,MAAM,SAAA,GAAY,WAAA,CAAY,CAAC,QAAA,KAAyB;AACtD,IAAA,OAAO,kBAAkB,QAAQ,CAAA;AAAA,EACnC,CAAA,EAAG,EAAE,CAAA;AACL,EAAA,OAAO,oBAAA,CAAqB,SAAA,EAAW,iBAAA,EAAmB,MAAM,IAAI,CAAA;AACtE;AALgB,MAAA,CAAA,iBAAA,EAAA,mBAAA,CAAA","file":"index.mjs","sourcesContent":["'use client';\n\nimport { useCallback, useSyncExternalStore } from 'react';\n\n/**\n * Minimal imperative handle every text-editor surface implements so\n * an external tool (voice dictation, command palette, AI suggestion)\n * can read/write its text content without traversing React.\n *\n * Methods are optional so a host can register a partial handle\n * (e.g. only `getValue` + `setValue`), and the caller checks before use.\n */\nexport interface ComposerHandle {\n /** Move keyboard focus into the composer's editable surface. */\n focus: () => void;\n /** Move the caret to the very end of the input. */\n moveCursorToEnd?: () => void;\n /** Read the current draft text. Voice dictation anchors partial\n * transcripts onto the user's already-typed prefix via this. */\n getValue?: () => string;\n /** Replace the current draft text. Voice dictation pushes interim\n * and final transcripts through this without owning a controlled\n * binding. */\n setValue?: (value: string) => void;\n}\n\n/**\n * `@djangocfg/ui-tools/composer-registry`\n *\n * Cross-tool bridge: the currently-active text composer's handle.\n *\n * Producer side (`@djangocfg/ui-tools/chat` and TipTap hosts):\n * register their composer's imperative handle via `attachComposer`.\n *\n * Consumer side (`@djangocfg/ui-tools/speech-recognition`):\n * reads the active handle via `useActiveComposer`/`getActiveComposer`\n * and pipes voice transcripts into it.\n *\n * Why this lives in its own subpath (not inside `chat`)\n * ----------------------------------------------------\n * `chat` and `speech-recognition` are sibling subpath exports. If the\n * registry lived inside `chat`, then `speech-recognition` would have\n * to reach into it via a cross-tool relative import — and under Vite\n * dev's dependency optimizer that file ends up loaded TWICE (once via\n * the `./chat` URL, once via the `./speech-recognition` relative-up\n * URL), giving the producer and the consumer two separate `let active`\n * slots. The active handle registered by chat would be invisible to\n * speech-recognition (and vice versa).\n *\n * Putting the registry in its own dedicated subpath (a single tool\n * that NEITHER chat nor speech-recognition cross-import — they both\n * import this one as their dependency) means Vite resolves it from a\n * single URL across the whole graph. One module instance, one shared\n * `active` slot.\n *\n * Semantics: one active composer per realm. The most recent\n * `registerComposer(handle)` wins; `registerComposer(null)` clears it.\n */\n\ntype Listener = (handle: ComposerHandle | null) => void;\n\nlet active: ComposerHandle | null = null;\nconst listeners = new Set<Listener>();\n\n/** Set or replace the active composer handle. Pass `null` to clear. */\nexport function registerComposer(handle: ComposerHandle | null): void {\n active = handle;\n for (const fn of listeners) fn(active);\n}\n\n/**\n * Convenience for components: register on mount, unregister on\n * unmount. Returns a cleanup function suitable for `useEffect`.\n */\nexport function attachComposer(handle: ComposerHandle): () => void {\n registerComposer(handle);\n return () => {\n if (active === handle) registerComposer(null);\n };\n}\n\n/** Read the current active handle (no subscription). */\nexport function getActiveComposer(): ComposerHandle | null {\n return active;\n}\n\n/** Subscribe to handle changes; returns an unsubscribe fn. */\nexport function subscribeComposer(listener: Listener): () => void {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n\n/**\n * React hook: re-renders the caller whenever the active composer\n * changes. Built on `useSyncExternalStore` so concurrent rendering,\n * SSR, and dev-mode strict-effects all behave correctly.\n */\nexport function useActiveComposer(): ComposerHandle | null {\n const subscribe = useCallback((onChange: () => void) => {\n return subscribeComposer(onChange);\n }, []);\n return useSyncExternalStore(subscribe, getActiveComposer, () => null);\n}\n"]}
|
package/dist/tree/index.cjs
CHANGED
|
@@ -5,9 +5,9 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
5
5
|
var chunkPK6SKIKE_cjs = require('../chunk-PK6SKIKE.cjs');
|
|
6
6
|
var React = require('react');
|
|
7
7
|
var lib = require('@djangocfg/ui-core/lib');
|
|
8
|
-
var components = require('@djangocfg/ui-core/components');
|
|
9
8
|
var dialogService = require('@djangocfg/ui-core/lib/dialog-service');
|
|
10
9
|
var lucideReact = require('lucide-react');
|
|
10
|
+
var components = require('@djangocfg/ui-core/components');
|
|
11
11
|
var jsxRuntime = require('react/jsx-runtime');
|
|
12
12
|
var core = require('@dnd-kit/core');
|
|
13
13
|
var hooks = require('@djangocfg/ui-core/hooks');
|
|
@@ -1071,6 +1071,47 @@ function useResolvedMenu(opts) {
|
|
|
1071
1071
|
]);
|
|
1072
1072
|
}
|
|
1073
1073
|
chunkPK6SKIKE_cjs.__name(useResolvedMenu, "useResolvedMenu");
|
|
1074
|
+
function renderItemsAsContextMenu(rowProps, items, trigger) {
|
|
1075
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(components.ContextMenu, { children: [
|
|
1076
|
+
/* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuTrigger, { asChild: true, children: trigger }),
|
|
1077
|
+
/* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuContent, { children: items.map((item, idx) => {
|
|
1078
|
+
if (item === "separator") {
|
|
1079
|
+
return /* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuSeparator, {}, `sep-${idx}`);
|
|
1080
|
+
}
|
|
1081
|
+
const Icon = item.icon;
|
|
1082
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1083
|
+
components.ContextMenuItem,
|
|
1084
|
+
{
|
|
1085
|
+
disabled: item.disabled,
|
|
1086
|
+
variant: item.destructive ? "destructive" : void 0,
|
|
1087
|
+
onSelect: () => item.onSelect(rowProps),
|
|
1088
|
+
children: [
|
|
1089
|
+
Icon ? /* @__PURE__ */ jsxRuntime.jsx(Icon, {}) : null,
|
|
1090
|
+
item.label,
|
|
1091
|
+
item.shortcut ? /* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuShortcut, { children: item.shortcut }) : null
|
|
1092
|
+
]
|
|
1093
|
+
},
|
|
1094
|
+
item.id
|
|
1095
|
+
);
|
|
1096
|
+
}) })
|
|
1097
|
+
] });
|
|
1098
|
+
}
|
|
1099
|
+
chunkPK6SKIKE_cjs.__name(renderItemsAsContextMenu, "renderItemsAsContextMenu");
|
|
1100
|
+
function tidyMenuItems(items) {
|
|
1101
|
+
const out = [];
|
|
1102
|
+
for (const it of items) {
|
|
1103
|
+
if (it === "separator") {
|
|
1104
|
+
if (out.length === 0) continue;
|
|
1105
|
+
if (out[out.length - 1] === "separator") continue;
|
|
1106
|
+
out.push(it);
|
|
1107
|
+
} else {
|
|
1108
|
+
out.push(it);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
while (out.length > 0 && out[out.length - 1] === "separator") out.pop();
|
|
1112
|
+
return out;
|
|
1113
|
+
}
|
|
1114
|
+
chunkPK6SKIKE_cjs.__name(tidyMenuItems, "tidyMenuItems");
|
|
1074
1115
|
|
|
1075
1116
|
// src/tools/data/Tree/data/dnd.ts
|
|
1076
1117
|
function resolveDropZone(input) {
|
|
@@ -1435,6 +1476,20 @@ function TreeProvider(props) {
|
|
|
1435
1476
|
copyToClipboard: clipboard.copyToClipboard,
|
|
1436
1477
|
pasteFromClipboard: clipboard.pasteFromClipboard
|
|
1437
1478
|
});
|
|
1479
|
+
const finalRenderContextMenu = React.useMemo(
|
|
1480
|
+
() => {
|
|
1481
|
+
if (renderContextMenu) return renderContextMenu;
|
|
1482
|
+
const resolve = resolvedContextMenuActions;
|
|
1483
|
+
if (!resolve) return void 0;
|
|
1484
|
+
return (rowProps, trigger) => {
|
|
1485
|
+
const items = resolve(rowProps);
|
|
1486
|
+
const cleaned = items ? tidyMenuItems(items) : null;
|
|
1487
|
+
if (!cleaned || cleaned.length === 0) return trigger;
|
|
1488
|
+
return renderItemsAsContextMenu(rowProps, cleaned, trigger);
|
|
1489
|
+
};
|
|
1490
|
+
},
|
|
1491
|
+
[renderContextMenu, resolvedContextMenuActions]
|
|
1492
|
+
);
|
|
1438
1493
|
const value = React.useMemo(
|
|
1439
1494
|
() => ({
|
|
1440
1495
|
// state
|
|
@@ -1476,7 +1531,7 @@ function TreeProvider(props) {
|
|
|
1476
1531
|
renderIcon,
|
|
1477
1532
|
renderLabel,
|
|
1478
1533
|
renderActions,
|
|
1479
|
-
renderContextMenu,
|
|
1534
|
+
renderContextMenu: finalRenderContextMenu,
|
|
1480
1535
|
adapter,
|
|
1481
1536
|
resolvedContextMenuActions,
|
|
1482
1537
|
getNodeById: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((id) => nodeById.get(id), "getNodeById"),
|
|
@@ -1513,7 +1568,7 @@ function TreeProvider(props) {
|
|
|
1513
1568
|
renderIcon,
|
|
1514
1569
|
renderLabel,
|
|
1515
1570
|
renderActions,
|
|
1516
|
-
|
|
1571
|
+
finalRenderContextMenu,
|
|
1517
1572
|
adapter,
|
|
1518
1573
|
resolvedContextMenuActions,
|
|
1519
1574
|
nodeById,
|
|
@@ -1940,6 +1995,9 @@ function TreeRowRaw({ row, className }) {
|
|
|
1940
1995
|
},
|
|
1941
1996
|
...dnd.active ? draggable.listeners : {},
|
|
1942
1997
|
...dnd.active ? draggable.attributes : {},
|
|
1998
|
+
onMouseDown: (e) => {
|
|
1999
|
+
if (e.button === 0) e.preventDefault();
|
|
2000
|
+
},
|
|
1943
2001
|
onClick: handleClick,
|
|
1944
2002
|
onDoubleClick: handleDoubleClick,
|
|
1945
2003
|
onContextMenu: handleContextMenu,
|
|
@@ -2789,47 +2847,6 @@ function useTreeFinderHotkeys(opts) {
|
|
|
2789
2847
|
return { ref };
|
|
2790
2848
|
}
|
|
2791
2849
|
chunkPK6SKIKE_cjs.__name(useTreeFinderHotkeys, "useTreeFinderHotkeys");
|
|
2792
|
-
function renderItemsAsContextMenu(rowProps, items, trigger) {
|
|
2793
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(components.ContextMenu, { children: [
|
|
2794
|
-
/* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuTrigger, { asChild: true, children: trigger }),
|
|
2795
|
-
/* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuContent, { children: items.map((item, idx) => {
|
|
2796
|
-
if (item === "separator") {
|
|
2797
|
-
return /* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuSeparator, {}, `sep-${idx}`);
|
|
2798
|
-
}
|
|
2799
|
-
const Icon = item.icon;
|
|
2800
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2801
|
-
components.ContextMenuItem,
|
|
2802
|
-
{
|
|
2803
|
-
disabled: item.disabled,
|
|
2804
|
-
variant: item.destructive ? "destructive" : void 0,
|
|
2805
|
-
onSelect: () => item.onSelect(rowProps),
|
|
2806
|
-
children: [
|
|
2807
|
-
Icon ? /* @__PURE__ */ jsxRuntime.jsx(Icon, {}) : null,
|
|
2808
|
-
item.label,
|
|
2809
|
-
item.shortcut ? /* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuShortcut, { children: item.shortcut }) : null
|
|
2810
|
-
]
|
|
2811
|
-
},
|
|
2812
|
-
item.id
|
|
2813
|
-
);
|
|
2814
|
-
}) })
|
|
2815
|
-
] });
|
|
2816
|
-
}
|
|
2817
|
-
chunkPK6SKIKE_cjs.__name(renderItemsAsContextMenu, "renderItemsAsContextMenu");
|
|
2818
|
-
function tidyMenuItems(items) {
|
|
2819
|
-
const out = [];
|
|
2820
|
-
for (const it of items) {
|
|
2821
|
-
if (it === "separator") {
|
|
2822
|
-
if (out.length === 0) continue;
|
|
2823
|
-
if (out[out.length - 1] === "separator") continue;
|
|
2824
|
-
out.push(it);
|
|
2825
|
-
} else {
|
|
2826
|
-
out.push(it);
|
|
2827
|
-
}
|
|
2828
|
-
}
|
|
2829
|
-
while (out.length > 0 && out[out.length - 1] === "separator") out.pop();
|
|
2830
|
-
return out;
|
|
2831
|
-
}
|
|
2832
|
-
chunkPK6SKIKE_cjs.__name(tidyMenuItems, "tidyMenuItems");
|
|
2833
2850
|
function TreeRoot(props) {
|
|
2834
2851
|
const {
|
|
2835
2852
|
data,
|
|
@@ -2863,6 +2880,7 @@ function TreeRoot(props) {
|
|
|
2863
2880
|
persistSelection = false,
|
|
2864
2881
|
adapter,
|
|
2865
2882
|
defaultMenuItems,
|
|
2883
|
+
actionsRef,
|
|
2866
2884
|
className,
|
|
2867
2885
|
style
|
|
2868
2886
|
} = props;
|
|
@@ -2905,7 +2923,8 @@ function TreeRoot(props) {
|
|
|
2905
2923
|
enableSearch,
|
|
2906
2924
|
enableTypeAhead,
|
|
2907
2925
|
enableFinderHotkeys,
|
|
2908
|
-
renderRow
|
|
2926
|
+
renderRow,
|
|
2927
|
+
actionsRef
|
|
2909
2928
|
}
|
|
2910
2929
|
)
|
|
2911
2930
|
}
|
|
@@ -2918,10 +2937,29 @@ function TreeRootShell({
|
|
|
2918
2937
|
enableSearch,
|
|
2919
2938
|
enableTypeAhead,
|
|
2920
2939
|
enableFinderHotkeys,
|
|
2921
|
-
renderRow
|
|
2940
|
+
renderRow,
|
|
2941
|
+
actionsRef
|
|
2922
2942
|
}) {
|
|
2923
2943
|
const containerRef = React.useRef(null);
|
|
2924
2944
|
const ctx = useTreeContext();
|
|
2945
|
+
React.useEffect(() => {
|
|
2946
|
+
if (!actionsRef) return;
|
|
2947
|
+
actionsRef.current = {
|
|
2948
|
+
refresh: ctx.refresh,
|
|
2949
|
+
refreshAll: ctx.refreshAll,
|
|
2950
|
+
expandAll: ctx.expandAll,
|
|
2951
|
+
collapseAll: ctx.collapseAll
|
|
2952
|
+
};
|
|
2953
|
+
return () => {
|
|
2954
|
+
if (actionsRef.current) actionsRef.current = null;
|
|
2955
|
+
};
|
|
2956
|
+
}, [
|
|
2957
|
+
actionsRef,
|
|
2958
|
+
ctx.refresh,
|
|
2959
|
+
ctx.refreshAll,
|
|
2960
|
+
ctx.expandAll,
|
|
2961
|
+
ctx.collapseAll
|
|
2962
|
+
]);
|
|
2925
2963
|
const isMulti = ctx.selectionMode === "multiple";
|
|
2926
2964
|
const { ref: keyboardRef } = useTreeKeyboard({
|
|
2927
2965
|
rows: ctx.flatRows,
|
|
@@ -2998,21 +3036,6 @@ function TreeRootShell({
|
|
|
2998
3036
|
onMatch: onTypeAheadMatch,
|
|
2999
3037
|
enabled: enableTypeAhead
|
|
3000
3038
|
});
|
|
3001
|
-
const finalRenderContextMenu = React.useMemo(() => {
|
|
3002
|
-
if (ctx.renderContextMenu) return ctx.renderContextMenu;
|
|
3003
|
-
const resolve = ctx.resolvedContextMenuActions;
|
|
3004
|
-
if (!resolve) return void 0;
|
|
3005
|
-
return (rowProps, trigger) => {
|
|
3006
|
-
const items = resolve(rowProps);
|
|
3007
|
-
const cleaned = items ? tidyMenuItems(items) : null;
|
|
3008
|
-
if (!cleaned || cleaned.length === 0) return trigger;
|
|
3009
|
-
return renderItemsAsContextMenu(rowProps, cleaned, trigger);
|
|
3010
|
-
};
|
|
3011
|
-
}, [ctx.renderContextMenu, ctx.resolvedContextMenuActions]);
|
|
3012
|
-
const childCtx = React.useMemo(
|
|
3013
|
-
() => ({ ...ctx, renderContextMenu: finalRenderContextMenu }),
|
|
3014
|
-
[ctx, finalRenderContextMenu]
|
|
3015
|
-
);
|
|
3016
3039
|
const treeBody = /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3017
3040
|
"div",
|
|
3018
3041
|
{
|
|
@@ -3038,8 +3061,7 @@ function TreeRootShell({
|
|
|
3038
3061
|
]
|
|
3039
3062
|
}
|
|
3040
3063
|
);
|
|
3041
|
-
|
|
3042
|
-
return /* @__PURE__ */ jsxRuntime.jsx(TreeDndProvider, { children: body });
|
|
3064
|
+
return /* @__PURE__ */ jsxRuntime.jsx(TreeDndProvider, { children: treeBody });
|
|
3043
3065
|
}
|
|
3044
3066
|
chunkPK6SKIKE_cjs.__name(TreeRootShell, "TreeRootShell");
|
|
3045
3067
|
var TreeRoot_default = TreeRoot;
|