@djangocfg/ui-tools 2.1.415 → 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.
Files changed (140) hide show
  1. package/dist/audio-player/index.cjs +2099 -0
  2. package/dist/audio-player/index.cjs.map +1 -0
  3. package/dist/audio-player/index.css +65 -0
  4. package/dist/audio-player/index.css.map +1 -0
  5. package/dist/audio-player/index.d.cts +174 -0
  6. package/dist/audio-player/index.d.ts +174 -0
  7. package/dist/audio-player/index.mjs +2076 -0
  8. package/dist/audio-player/index.mjs.map +1 -0
  9. package/dist/composer-registry/index.cjs +45 -0
  10. package/dist/composer-registry/index.cjs.map +1 -0
  11. package/dist/composer-registry/index.d.cts +73 -0
  12. package/dist/composer-registry/index.d.ts +73 -0
  13. package/dist/composer-registry/index.mjs +39 -0
  14. package/dist/composer-registry/index.mjs.map +1 -0
  15. package/dist/file-icon/index.d.cts +1 -1
  16. package/dist/file-icon/index.d.ts +1 -1
  17. package/dist/slots-ClRpIzoh.d.cts +88 -0
  18. package/dist/slots-ClRpIzoh.d.ts +88 -0
  19. package/dist/tree/index.cjs +2019 -279
  20. package/dist/tree/index.cjs.map +1 -1
  21. package/dist/tree/index.d.cts +731 -72
  22. package/dist/tree/index.d.ts +731 -72
  23. package/dist/tree/index.mjs +2009 -282
  24. package/dist/tree/index.mjs.map +1 -1
  25. package/package.json +18 -9
  26. package/src/tools/chat/README.md +111 -1
  27. package/src/tools/chat/composer/Composer.tsx +146 -25
  28. package/src/tools/chat/composer/ComposerRichTextarea.tsx +25 -0
  29. package/src/tools/chat/composer/index.ts +22 -0
  30. package/src/tools/chat/composer/slash/README.md +187 -0
  31. package/src/tools/chat/composer/slash/SlashHighlightTextarea.tsx +144 -0
  32. package/src/tools/chat/composer/slash/SlashMenu.tsx +142 -0
  33. package/src/tools/chat/composer/slash/SlashToken.tsx +57 -0
  34. package/src/tools/chat/composer/slash/index.ts +44 -0
  35. package/src/tools/chat/composer/slash/labels.ts +19 -0
  36. package/src/tools/chat/composer/slash/state.ts +168 -0
  37. package/src/tools/chat/composer/slash/types.ts +64 -0
  38. package/src/tools/chat/composer/slash/useSlashCommands.ts +204 -0
  39. package/src/tools/chat/composer/types.ts +8 -0
  40. package/src/tools/chat/context/ChatProvider.tsx +13 -78
  41. package/src/tools/chat/hooks/useAutoFocusOnStreamEnd.ts +12 -15
  42. package/src/tools/chat/hooks/useFocusOnEmptyClick.ts +4 -5
  43. package/src/tools/chat/launcher/header/ChatHeader.tsx +14 -19
  44. package/src/tools/chat/launcher/header/ChatHeaderActionButton.tsx +8 -12
  45. package/src/tools/chat/shell/SuggestedPrompts.tsx +194 -0
  46. package/src/tools/chat/shell/index.ts +6 -0
  47. package/src/tools/data/Listbox/lazy.tsx +1 -1
  48. package/src/tools/data/Masonry/lazy.tsx +1 -1
  49. package/src/tools/data/Timeline/lazy.tsx +1 -1
  50. package/src/tools/data/Tree/FinderTree.tsx +42 -0
  51. package/src/tools/data/Tree/README.md +337 -208
  52. package/src/tools/data/Tree/TreeDndProvider.tsx +137 -0
  53. package/src/tools/data/Tree/TreeRoot.tsx +111 -72
  54. package/src/tools/data/Tree/__tests__/dnd.test.ts +160 -0
  55. package/src/tools/data/Tree/__tests__/keyboard.test.ts +137 -0
  56. package/src/tools/data/Tree/__tests__/renameUtils.test.ts +52 -0
  57. package/src/tools/data/Tree/__tests__/selection.test.ts +227 -0
  58. package/src/tools/data/Tree/components/TreeDropIndicator.tsx +65 -0
  59. package/src/tools/data/Tree/components/TreeEmptyArea.tsx +160 -0
  60. package/src/tools/data/Tree/components/TreeRenameInput.tsx +114 -0
  61. package/src/tools/data/Tree/components/TreeRow.tsx +103 -8
  62. package/src/tools/data/Tree/components/index.ts +6 -0
  63. package/src/tools/data/Tree/context/TreeContext.tsx +223 -363
  64. package/src/tools/data/Tree/context/TreeContextValue.ts +139 -0
  65. package/src/tools/data/Tree/context/async-children/collect-ids.ts +27 -0
  66. package/src/tools/data/Tree/context/async-children/index.ts +8 -0
  67. package/src/tools/data/Tree/context/async-children/use-async-children.ts +157 -0
  68. package/src/tools/data/Tree/context/clipboard/index.ts +4 -0
  69. package/src/tools/data/Tree/context/clipboard/use-clipboard.ts +115 -0
  70. package/src/tools/data/Tree/context/dnd/index.ts +8 -0
  71. package/src/tools/data/Tree/context/dnd/use-dnd.ts +194 -0
  72. package/src/tools/data/Tree/context/expansion/index.ts +4 -0
  73. package/src/tools/data/Tree/context/expansion/use-expansion.ts +55 -0
  74. package/src/tools/data/Tree/context/hooks.ts +68 -1
  75. package/src/tools/data/Tree/context/index.ts +3 -0
  76. package/src/tools/data/Tree/context/menu/builtin-actions.ts +357 -0
  77. package/src/tools/data/Tree/context/menu/index.ts +11 -0
  78. package/src/tools/data/Tree/context/menu/render.tsx +75 -0
  79. package/src/tools/data/Tree/context/menu/use-resolved-menu.ts +141 -0
  80. package/src/tools/data/Tree/context/persist/index.ts +4 -0
  81. package/src/tools/data/Tree/context/persist/use-persist-sync.ts +74 -0
  82. package/src/tools/data/Tree/context/rename/index.ts +4 -0
  83. package/src/tools/data/Tree/context/rename/use-rename.ts +113 -0
  84. package/src/tools/data/Tree/context/selection/index.ts +4 -0
  85. package/src/tools/data/Tree/context/selection/use-selection.ts +146 -0
  86. package/src/tools/data/Tree/context/state/index.ts +6 -0
  87. package/src/tools/data/Tree/context/state/initial.ts +41 -0
  88. package/src/tools/data/Tree/context/state/reducer.ts +76 -0
  89. package/src/tools/data/Tree/context/state/types.ts +46 -0
  90. package/src/tools/data/Tree/data/clipboard.ts +33 -0
  91. package/src/tools/data/Tree/data/dnd.ts +123 -0
  92. package/src/tools/data/Tree/data/finderShortcuts.ts +67 -0
  93. package/src/tools/data/Tree/data/index.ts +19 -0
  94. package/src/tools/data/Tree/data/renameUtils.ts +51 -0
  95. package/src/tools/data/Tree/data/selection.ts +157 -0
  96. package/src/tools/data/Tree/hooks/finder-hotkeys/build-ctx.ts +48 -0
  97. package/src/tools/data/Tree/hooks/finder-hotkeys/index.ts +8 -0
  98. package/src/tools/data/Tree/hooks/finder-hotkeys/use-tree-finder-hotkeys.ts +166 -0
  99. package/src/tools/data/Tree/hooks/index.ts +23 -4
  100. package/src/tools/data/Tree/hooks/keyboard/activation.ts +27 -0
  101. package/src/tools/data/Tree/hooks/keyboard/arrow-nav.ts +26 -0
  102. package/src/tools/data/Tree/hooks/keyboard/expand-collapse.ts +54 -0
  103. package/src/tools/data/Tree/hooks/keyboard/index.ts +10 -0
  104. package/src/tools/data/Tree/hooks/keyboard/types.ts +39 -0
  105. package/src/tools/data/Tree/hooks/keyboard/use-tree-keyboard.ts +196 -0
  106. package/src/tools/data/Tree/hooks/type-ahead/index.ts +5 -0
  107. package/src/tools/data/Tree/hooks/type-ahead/match-prefix.ts +42 -0
  108. package/src/tools/data/Tree/hooks/{useTreeTypeAhead.ts → type-ahead/use-tree-type-ahead.ts} +8 -19
  109. package/src/tools/data/Tree/index.tsx +26 -2
  110. package/src/tools/data/Tree/types/activation.ts +30 -0
  111. package/src/tools/data/Tree/types/adapter.ts +70 -0
  112. package/src/tools/data/Tree/types/index.ts +27 -0
  113. package/src/tools/data/Tree/types/labels.ts +97 -0
  114. package/src/tools/data/Tree/types/loader.ts +9 -0
  115. package/src/tools/data/Tree/types/node.ts +38 -0
  116. package/src/tools/data/Tree/types/root-props.ts +158 -0
  117. package/src/tools/data/Tree/types/selection.ts +3 -0
  118. package/src/tools/data/Tree/types/slots.ts +64 -0
  119. package/src/tools/dev/OpenapiViewer/components/DocsLayout/EndpointDoc/Header/MetaActions.tsx +6 -9
  120. package/src/tools/dev/OpenapiViewer/components/DocsLayout/index.tsx +2 -4
  121. package/src/tools/forms/MarkdownEditor/MarkdownEditor.tsx +85 -0
  122. package/src/tools/forms/MarkdownEditor/index.ts +1 -0
  123. package/src/tools/forms/MarkdownEditor/lazy.tsx +6 -0
  124. package/src/tools/forms/MarkdownEditor/slash/SlashCommandNode.ts +162 -0
  125. package/src/tools/forms/MarkdownEditor/slash/index.ts +4 -0
  126. package/src/tools/forms/MarkdownEditor/slash/syncSlashNode.ts +97 -0
  127. package/src/tools/forms/MarkdownEditor/slash/types.ts +13 -0
  128. package/src/tools/forms/MarkdownEditor/styles.css +18 -0
  129. package/src/tools/input/SpeechRecognition/widgets/VoiceComposerSlot.tsx +11 -12
  130. package/src/tools/integration/ComposerRegistry/index.ts +105 -0
  131. package/src/tools/media/AudioPlayer/Player.tsx +2 -0
  132. package/src/tools/media/AudioPlayer/PlayerShell.tsx +37 -22
  133. package/src/tools/media/AudioPlayer/lazy.tsx +30 -42
  134. package/src/tools/media/AudioPlayer/parts/Controls/IconButton.tsx +10 -11
  135. package/src/tools/media/AudioPlayer/parts/Controls/VolumeControl.tsx +52 -115
  136. package/src/tools/media/AudioPlayer/types.ts +15 -0
  137. package/dist/types-j2vhn4Kv.d.cts +0 -241
  138. package/dist/types-j2vhn4Kv.d.ts +0 -241
  139. package/src/tools/data/Tree/hooks/useTreeKeyboard.ts +0 -171
  140. package/src/tools/data/Tree/types.ts +0 -217
@@ -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"]}
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { LucideIcon } from 'lucide-react';
3
- import { b as TreeNode, g as TreeRowSlot } from '../types-j2vhn4Kv.cjs';
3
+ import { T as TreeNode, b as TreeRowSlot } from '../slots-ClRpIzoh.cjs';
4
4
  import 'react';
5
5
 
6
6
  interface ResolveFolderIconOptions {
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { LucideIcon } from 'lucide-react';
3
- import { b as TreeNode, g as TreeRowSlot } from '../types-j2vhn4Kv.js';
3
+ import { T as TreeNode, b as TreeRowSlot } from '../slots-ClRpIzoh.js';
4
4
  import 'react';
5
5
 
6
6
  interface ResolveFolderIconOptions {
@@ -0,0 +1,88 @@
1
+ import { ReactNode, ComponentType } from 'react';
2
+
3
+ type TreeItemId = string;
4
+ /** A single node in the consumer's tree data. Generic over your payload. */
5
+ interface TreeNode<T = unknown> {
6
+ id: TreeItemId;
7
+ data: T;
8
+ /** Inline children. Omit (and provide a `loadChildren`) for async loading. */
9
+ children?: TreeNode<T>[];
10
+ /**
11
+ * Set to `true` to mark a node as a folder even when its `children` array
12
+ * is empty (e.g. an unloaded async folder). Default: derived from
13
+ * `Array.isArray(children)`.
14
+ */
15
+ isFolder?: boolean;
16
+ /** Disable interaction. */
17
+ disabled?: boolean;
18
+ }
19
+ /**
20
+ * Internal flat-row representation used by the renderer + keyboard nav.
21
+ * Produced by `flattenTree`; consumed by `TreeRow`, the keyboard hook,
22
+ * and the type-ahead hook.
23
+ */
24
+ interface FlatRow<T> {
25
+ node: TreeNode<T>;
26
+ level: number;
27
+ parentId: TreeItemId | null;
28
+ isFolder: boolean;
29
+ isExpanded: boolean;
30
+ isLoading: boolean;
31
+ hasError: boolean;
32
+ /** 1-based position among visible siblings (for `aria-posinset`). */
33
+ posInSet: number;
34
+ /** Count of visible siblings sharing this row's parent (for `aria-setsize`). */
35
+ setSize: number;
36
+ }
37
+
38
+ interface TreeRowRenderProps<T> {
39
+ node: TreeNode<T>;
40
+ level: number;
41
+ isSelected: boolean;
42
+ isExpanded: boolean;
43
+ isFocused: boolean;
44
+ isFolder: boolean;
45
+ isLoading: boolean;
46
+ isMatchingSearch: boolean;
47
+ }
48
+ type TreeRowSlot<T> = (props: TreeRowRenderProps<T>) => ReactNode;
49
+ type TreeContextMenuSlot<T> = (props: TreeRowRenderProps<T>, trigger: ReactNode) => ReactNode;
50
+ /**
51
+ * Declarative context-menu item. Pass `'separator'` (string) in place of an
52
+ * object to insert a `<ContextMenuSeparator />` between groups.
53
+ *
54
+ * For more advanced needs (submenus, checkbox items, custom JSX), drop down
55
+ * to `renderContextMenu` instead.
56
+ */
57
+ interface TreeContextMenuAction<T> {
58
+ /** Stable React key. */
59
+ id: string;
60
+ label: ReactNode;
61
+ /** Lucide-style icon component. Rendered as `<icon className="size-4" />`. */
62
+ icon?: ComponentType<{
63
+ className?: string;
64
+ }>;
65
+ /** Right-aligned keyboard hint (e.g. `'⌘C'`, `'↵'`). Cosmetic. */
66
+ shortcut?: ReactNode;
67
+ /** Disable the item — still rendered, not selectable. */
68
+ disabled?: boolean;
69
+ /** Style as destructive (red). */
70
+ destructive?: boolean;
71
+ /** Click / Enter handler. Receives the row meta. */
72
+ onSelect: (props: TreeRowRenderProps<T>) => void;
73
+ }
74
+ type TreeContextMenuItem<T> = TreeContextMenuAction<T> | 'separator';
75
+ /**
76
+ * Context passed to a `contextMenuActions` resolver. `row` is the row the
77
+ * user right-clicked. `selectedNodes` is the multi-selection at the
78
+ * moment the menu opens — when the right-clicked row was not in the
79
+ * selection, Tree first switches selection to that single row, so
80
+ * `selectedNodes` always contains the right-clicked node.
81
+ */
82
+ interface TreeContextMenuActionsContext<T> extends TreeRowRenderProps<T> {
83
+ /** Multi-selection at the moment the menu opens. Always non-empty. */
84
+ selectedNodes: TreeNode<T>[];
85
+ }
86
+ type TreeContextMenuActionsResolver<T> = (ctx: TreeContextMenuActionsContext<T>) => TreeContextMenuItem<T>[] | null | undefined;
87
+
88
+ export type { FlatRow as F, TreeNode as T, TreeItemId as a, TreeRowSlot as b, TreeContextMenuSlot as c, TreeContextMenuActionsResolver as d, TreeRowRenderProps as e, TreeContextMenuItem as f, TreeContextMenuAction as g, TreeContextMenuActionsContext as h };
@@ -0,0 +1,88 @@
1
+ import { ReactNode, ComponentType } from 'react';
2
+
3
+ type TreeItemId = string;
4
+ /** A single node in the consumer's tree data. Generic over your payload. */
5
+ interface TreeNode<T = unknown> {
6
+ id: TreeItemId;
7
+ data: T;
8
+ /** Inline children. Omit (and provide a `loadChildren`) for async loading. */
9
+ children?: TreeNode<T>[];
10
+ /**
11
+ * Set to `true` to mark a node as a folder even when its `children` array
12
+ * is empty (e.g. an unloaded async folder). Default: derived from
13
+ * `Array.isArray(children)`.
14
+ */
15
+ isFolder?: boolean;
16
+ /** Disable interaction. */
17
+ disabled?: boolean;
18
+ }
19
+ /**
20
+ * Internal flat-row representation used by the renderer + keyboard nav.
21
+ * Produced by `flattenTree`; consumed by `TreeRow`, the keyboard hook,
22
+ * and the type-ahead hook.
23
+ */
24
+ interface FlatRow<T> {
25
+ node: TreeNode<T>;
26
+ level: number;
27
+ parentId: TreeItemId | null;
28
+ isFolder: boolean;
29
+ isExpanded: boolean;
30
+ isLoading: boolean;
31
+ hasError: boolean;
32
+ /** 1-based position among visible siblings (for `aria-posinset`). */
33
+ posInSet: number;
34
+ /** Count of visible siblings sharing this row's parent (for `aria-setsize`). */
35
+ setSize: number;
36
+ }
37
+
38
+ interface TreeRowRenderProps<T> {
39
+ node: TreeNode<T>;
40
+ level: number;
41
+ isSelected: boolean;
42
+ isExpanded: boolean;
43
+ isFocused: boolean;
44
+ isFolder: boolean;
45
+ isLoading: boolean;
46
+ isMatchingSearch: boolean;
47
+ }
48
+ type TreeRowSlot<T> = (props: TreeRowRenderProps<T>) => ReactNode;
49
+ type TreeContextMenuSlot<T> = (props: TreeRowRenderProps<T>, trigger: ReactNode) => ReactNode;
50
+ /**
51
+ * Declarative context-menu item. Pass `'separator'` (string) in place of an
52
+ * object to insert a `<ContextMenuSeparator />` between groups.
53
+ *
54
+ * For more advanced needs (submenus, checkbox items, custom JSX), drop down
55
+ * to `renderContextMenu` instead.
56
+ */
57
+ interface TreeContextMenuAction<T> {
58
+ /** Stable React key. */
59
+ id: string;
60
+ label: ReactNode;
61
+ /** Lucide-style icon component. Rendered as `<icon className="size-4" />`. */
62
+ icon?: ComponentType<{
63
+ className?: string;
64
+ }>;
65
+ /** Right-aligned keyboard hint (e.g. `'⌘C'`, `'↵'`). Cosmetic. */
66
+ shortcut?: ReactNode;
67
+ /** Disable the item — still rendered, not selectable. */
68
+ disabled?: boolean;
69
+ /** Style as destructive (red). */
70
+ destructive?: boolean;
71
+ /** Click / Enter handler. Receives the row meta. */
72
+ onSelect: (props: TreeRowRenderProps<T>) => void;
73
+ }
74
+ type TreeContextMenuItem<T> = TreeContextMenuAction<T> | 'separator';
75
+ /**
76
+ * Context passed to a `contextMenuActions` resolver. `row` is the row the
77
+ * user right-clicked. `selectedNodes` is the multi-selection at the
78
+ * moment the menu opens — when the right-clicked row was not in the
79
+ * selection, Tree first switches selection to that single row, so
80
+ * `selectedNodes` always contains the right-clicked node.
81
+ */
82
+ interface TreeContextMenuActionsContext<T> extends TreeRowRenderProps<T> {
83
+ /** Multi-selection at the moment the menu opens. Always non-empty. */
84
+ selectedNodes: TreeNode<T>[];
85
+ }
86
+ type TreeContextMenuActionsResolver<T> = (ctx: TreeContextMenuActionsContext<T>) => TreeContextMenuItem<T>[] | null | undefined;
87
+
88
+ export type { FlatRow as F, TreeNode as T, TreeItemId as a, TreeRowSlot as b, TreeContextMenuSlot as c, TreeContextMenuActionsResolver as d, TreeRowRenderProps as e, TreeContextMenuItem as f, TreeContextMenuAction as g, TreeContextMenuActionsContext as h };