@djangocfg/ui-tools 2.1.317 → 2.1.319

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 (65) hide show
  1. package/dist/{DocsLayout-ESVQZO3V.mjs → DocsLayout-CTJINVBM.mjs} +235 -267
  2. package/dist/DocsLayout-CTJINVBM.mjs.map +1 -0
  3. package/dist/{DocsLayout-KUPDWJ3G.cjs → DocsLayout-XLDB6CJ2.cjs} +273 -305
  4. package/dist/DocsLayout-XLDB6CJ2.cjs.map +1 -0
  5. package/dist/TreeRoot-A3J65L6F.mjs +4 -0
  6. package/dist/{TreeRoot-R6XVHYQK.mjs.map → TreeRoot-A3J65L6F.mjs.map} +1 -1
  7. package/dist/TreeRoot-DSK5JILT.cjs +19 -0
  8. package/dist/{TreeRoot-RAMQSBMO.cjs.map → TreeRoot-DSK5JILT.cjs.map} +1 -1
  9. package/dist/{chunk-44ZTWYAF.cjs → chunk-3Z3A7FHA.cjs} +17 -6
  10. package/dist/chunk-3Z3A7FHA.cjs.map +1 -0
  11. package/dist/{chunk-GBLQTHWT.mjs → chunk-62Y65TGK.mjs} +5 -4
  12. package/dist/chunk-62Y65TGK.mjs.map +1 -0
  13. package/dist/{chunk-NTJL2SXK.mjs → chunk-MOME6KYD.mjs} +17 -6
  14. package/dist/chunk-MOME6KYD.mjs.map +1 -0
  15. package/dist/{chunk-S44PW6NK.cjs → chunk-TKSFZHCG.cjs} +5 -4
  16. package/dist/chunk-TKSFZHCG.cjs.map +1 -0
  17. package/dist/file-icon/index.cjs +59 -3
  18. package/dist/file-icon/index.cjs.map +1 -1
  19. package/dist/file-icon/index.d.cts +33 -4
  20. package/dist/file-icon/index.d.ts +33 -4
  21. package/dist/file-icon/index.mjs +60 -5
  22. package/dist/file-icon/index.mjs.map +1 -1
  23. package/dist/index.cjs +43 -43
  24. package/dist/index.d.cts +2 -2
  25. package/dist/index.d.ts +2 -2
  26. package/dist/index.mjs +6 -6
  27. package/dist/tree/index.cjs +33 -33
  28. package/dist/tree/index.d.cts +6 -4
  29. package/dist/tree/index.d.ts +6 -4
  30. package/dist/tree/index.mjs +1 -1
  31. package/dist/{types-Cclwv4Hl.d.cts → types-CevSbyfD.d.cts} +6 -0
  32. package/dist/{types-Cclwv4Hl.d.ts → types-CevSbyfD.d.ts} +6 -0
  33. package/package.json +6 -6
  34. package/src/tools/FileIcon/FileIcon.tsx +13 -2
  35. package/src/tools/FileIcon/index.ts +6 -0
  36. package/src/tools/FileIcon/specialFolders.ts +93 -0
  37. package/src/tools/FileIcon/treeAdapter.tsx +8 -0
  38. package/src/tools/OpenapiViewer/OpenapiViewer.story.tsx +30 -0
  39. package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Header/MetaActions.tsx +35 -50
  40. package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Header/index.tsx +49 -22
  41. package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Section/index.tsx +1 -1
  42. package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/store/index.ts +10 -11
  43. package/src/tools/OpenapiViewer/components/DocsLayout/SchemaCopyMenu.tsx +25 -5
  44. package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/BrandHeader.tsx +18 -33
  45. package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/Toolbar.tsx +40 -24
  46. package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/index.tsx +8 -14
  47. package/src/tools/OpenapiViewer/components/DocsLayout/sidebarLabel.ts +1 -4
  48. package/src/tools/OpenapiViewer/utils/operationToHar.ts +2 -1
  49. package/src/tools/OpenapiViewer/utils/url.ts +9 -2
  50. package/src/tools/Tree/README.md +46 -2
  51. package/src/tools/Tree/Tree.story.tsx +36 -0
  52. package/src/tools/Tree/TreeRoot.tsx +2 -0
  53. package/src/tools/Tree/components/TreeContent.tsx +3 -1
  54. package/src/tools/Tree/context/TreeContext.tsx +4 -1
  55. package/src/tools/Tree/data/flatten.ts +10 -1
  56. package/src/tools/Tree/types.ts +7 -0
  57. package/dist/DocsLayout-ESVQZO3V.mjs.map +0 -1
  58. package/dist/DocsLayout-KUPDWJ3G.cjs.map +0 -1
  59. package/dist/TreeRoot-R6XVHYQK.mjs +0 -4
  60. package/dist/TreeRoot-RAMQSBMO.cjs +0 -19
  61. package/dist/chunk-44ZTWYAF.cjs.map +0 -1
  62. package/dist/chunk-GBLQTHWT.mjs.map +0 -1
  63. package/dist/chunk-NTJL2SXK.mjs.map +0 -1
  64. package/dist/chunk-S44PW6NK.cjs.map +0 -1
  65. package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/MethodChips.tsx +0 -43
@@ -200,6 +200,7 @@ Toggle the bar with `appearance.showActiveIndicator`. Intensity scales with `app
200
200
  | --- | --- |
201
201
  | Default | sensible cozy defaults |
202
202
  | WithActivationModes | single-click / double-click / preview semantics |
203
+ | WithHiddenFilter | `filterNode` toggle hides dot-files |
203
204
  | Densities | three density presets side-by-side |
204
205
  | WithIcons | file-type icons through `renderIcon` |
205
206
  | WithStatus | modified / error / disabled rows through `renderLabel` |
@@ -213,6 +214,35 @@ Toggle the bar with `appearance.showActiveIndicator`. Intensity scales with `app
213
214
  | LargeTree | ~500 nodes scalability check |
214
215
  | Playground | every knob exposed as a control |
215
216
 
217
+ ## Filtering nodes
218
+
219
+ `filterNode` is a single predicate that decides which nodes appear at all.
220
+ Nodes returning `false` (and their descendants) are excluded from rendering,
221
+ keyboard navigation, and search.
222
+
223
+ ```tsx
224
+ const [showHidden, setShowHidden] = useState(false);
225
+
226
+ <TreeRoot
227
+ data={data}
228
+ getItemName={(n) => n.data.name}
229
+ filterNode={(n) => showHidden || !n.data.name.startsWith('.')}
230
+ />
231
+ ```
232
+
233
+ This is intentionally minimal — Tree is generic over `T` and has no opinion
234
+ on what "hidden" means in your domain. If your backend already provides
235
+ flags like `entry.isHidden` / `entry.isSystem`, use them directly:
236
+
237
+ ```tsx
238
+ filterNode={(n) => showHidden || (!n.data.isHidden && !n.data.isSystem)}
239
+ ```
240
+
241
+ > **Frontend note.** From the browser you cannot read OS-level hidden
242
+ > attributes (Windows `FILE_ATTRIBUTE_HIDDEN`, macOS `kIsInvisible`).
243
+ > Either filter by name (Unix dot-prefix is the de-facto convention), or
244
+ > let your backend determine those flags and forward them in `node.data`.
245
+
216
246
  ## Activation modes
217
247
 
218
248
  How a leaf becomes "activated" (opened) on pointer interaction is controlled
@@ -264,8 +294,22 @@ import { createFileIconSlot } from '@djangocfg/ui-tools/file-icon';
264
294
 
265
295
  `material-file-icons` is declared in `optionalDependencies`. If it's not
266
296
  installed, `<FileIcon>` falls back to a Lucide `File` icon — no warnings, no
267
- runtime errors. Folders always render Lucide `Folder` / `FolderOpen` (amber
268
- tint), regardless of whether the optional package is present.
297
+ runtime errors.
298
+
299
+ Folders use a small built-in mapping (`src` → `FolderCode`,
300
+ `node_modules` → `Package`, `.git` → `FolderGit2`, `dist`/`build`/`.next`
301
+ → `FolderOutput`, `tests`/`__tests__` → `FlaskConical`, …). Anything not
302
+ in the table renders the generic `Folder` / `FolderOpen` pair. Override
303
+ or extend the table per-call:
304
+
305
+ ```tsx
306
+ import { FolderHeart } from 'lucide-react';
307
+
308
+ renderIcon={createFileIconSlot({
309
+ getName: (n) => n.data.name,
310
+ folderOverrides: { favorites: FolderHeart },
311
+ })}
312
+ ```
269
313
 
270
314
  ## Out of scope (today)
271
315
 
@@ -159,6 +159,42 @@ export const WithActivationModes = () => {
159
159
  );
160
160
  };
161
161
 
162
+ // ---------------------------------------------------------------------------
163
+ // 1.7) WithHiddenFilter — filterNode prop hides dot-prefixed entries
164
+ // ---------------------------------------------------------------------------
165
+
166
+ const fsWithDotfiles: TreeNode<FsNode>[] = [
167
+ ...fs,
168
+ { id: '.env', data: { name: '.env' } },
169
+ { id: '.gitignore', data: { name: '.gitignore' } },
170
+ {
171
+ id: '.git',
172
+ data: { name: '.git' },
173
+ isFolder: true,
174
+ children: [
175
+ { id: '.git/HEAD', data: { name: 'HEAD' } },
176
+ { id: '.git/config', data: { name: 'config' } },
177
+ ],
178
+ },
179
+ ];
180
+
181
+ export const WithHiddenFilter = () => {
182
+ const [showHidden] = useBoolean('showHidden', {
183
+ defaultValue: false,
184
+ label: 'Show hidden',
185
+ });
186
+ return (
187
+ <div className="h-96 w-80 rounded-md border border-border bg-card">
188
+ <TreeRoot<FsNode>
189
+ data={fsWithDotfiles}
190
+ getItemName={getName}
191
+ initialExpandedIds={['src']}
192
+ filterNode={(n) => showHidden || !n.data.name.startsWith('.')}
193
+ />
194
+ </div>
195
+ );
196
+ };
197
+
162
198
  // ---------------------------------------------------------------------------
163
199
  // 2) Densities — three presets side-by-side for comparison
164
200
  // ---------------------------------------------------------------------------
@@ -31,6 +31,7 @@ function TreeRoot<T>(props: TreeRootProps<T>) {
31
31
  onSelectionChange,
32
32
  onExpansionChange,
33
33
  onActivate,
34
+ filterNode,
34
35
  enableSearch = false,
35
36
  enableTypeAhead = true,
36
37
  showIndentGuides = false,
@@ -60,6 +61,7 @@ function TreeRoot<T>(props: TreeRootProps<T>) {
60
61
  onSelectionChange={onSelectionChange}
61
62
  onExpansionChange={onExpansionChange}
62
63
  onActivate={onActivate}
64
+ filterNode={filterNode}
63
65
  enableSearch={enableSearch}
64
66
  showIndentGuides={showIndentGuides}
65
67
  renderIcon={renderIcon}
@@ -4,6 +4,7 @@ import { Fragment, type ReactNode } from 'react';
4
4
  import { cn } from '@djangocfg/ui-core/lib';
5
5
 
6
6
  import { useTreeContext } from '../context/TreeContext';
7
+ import { appearanceToStyle } from '../data/appearance';
7
8
  import type { FlatRow, TreeRowRenderProps, TreeRowSlot } from '../types';
8
9
  import { TreeRow } from './TreeRow';
9
10
  import { TreeEmpty } from './TreeEmpty';
@@ -17,7 +18,7 @@ export interface TreeContentProps<T> {
17
18
  }
18
19
 
19
20
  export function TreeContent<T>({ children, className, ariaLabel }: TreeContentProps<T>) {
20
- const { flatRows, labels, selected, focused, matchingIds } = useTreeContext<T>();
21
+ const { flatRows, labels, selected, focused, matchingIds, appearance } = useTreeContext<T>();
21
22
 
22
23
  if (flatRows.length === 0) {
23
24
  return <TreeEmpty>{labels.empty}</TreeEmpty>;
@@ -28,6 +29,7 @@ export function TreeContent<T>({ children, className, ariaLabel }: TreeContentPr
28
29
  role="tree"
29
30
  aria-label={ariaLabel ?? labels.ariaLabel}
30
31
  className={cn('relative flex flex-col py-1', className)}
32
+ style={appearanceToStyle(appearance)}
31
33
  >
32
34
  {flatRows.map((row: FlatRow<T>) => {
33
35
  const slot: TreeRowRenderProps<T> = {
@@ -187,6 +187,7 @@ export interface TreeProviderProps<T>
187
187
  | 'onSelectionChange'
188
188
  | 'onExpansionChange'
189
189
  | 'onActivate'
190
+ | 'filterNode'
190
191
  | 'enableSearch'
191
192
  | 'showIndentGuides'
192
193
  | 'renderIcon'
@@ -237,6 +238,7 @@ export function TreeProvider<T>(props: TreeProviderProps<T>) {
237
238
  onSelectionChange,
238
239
  onExpansionChange,
239
240
  onActivate,
241
+ filterNode,
240
242
  enableSearch = false,
241
243
  showIndentGuides = false,
242
244
  renderIcon,
@@ -350,8 +352,9 @@ export function TreeProvider<T>(props: TreeProviderProps<T>) {
350
352
  roots: data,
351
353
  expandedIds: state.expanded,
352
354
  cache: cacheRef.current,
355
+ filterNode,
353
356
  }),
354
- [data, state.expanded, state.cacheTick],
357
+ [data, state.expanded, state.cacheTick, filterNode],
355
358
  );
356
359
 
357
360
  // Search matches (case-insensitive substring on getItemName).
@@ -7,6 +7,8 @@ export interface FlattenInput<T> {
7
7
  roots: TreeNode<T>[];
8
8
  expandedIds: ReadonlySet<TreeItemId>;
9
9
  cache: ChildCache<T>;
10
+ /** Optional predicate. Nodes returning `false` (and their descendants) are excluded. */
11
+ filterNode?: (node: TreeNode<T>) => boolean;
10
12
  }
11
13
 
12
14
  const isNodeFolder = <T>(node: TreeNode<T>): boolean => {
@@ -21,11 +23,18 @@ const isNodeFolder = <T>(node: TreeNode<T>): boolean => {
21
23
  * `expandedIds`. The output is ordered exactly as it should render,
22
24
  * which keeps keyboard navigation (next/prev row) trivial.
23
25
  */
24
- export function flattenTree<T>({ roots, expandedIds, cache }: FlattenInput<T>): FlatRow<T>[] {
26
+ export function flattenTree<T>({
27
+ roots,
28
+ expandedIds,
29
+ cache,
30
+ filterNode,
31
+ }: FlattenInput<T>): FlatRow<T>[] {
25
32
  const out: FlatRow<T>[] = [];
26
33
 
27
34
  const walk = (nodes: TreeNode<T>[], level: number, parentId: TreeItemId | null) => {
28
35
  for (const node of nodes) {
36
+ if (filterNode && !filterNode(node)) continue;
37
+
29
38
  const isFolder = isNodeFolder(node);
30
39
  const isExpanded = expandedIds.has(node.id);
31
40
  const resolved = isFolder ? resolveChildren(cache, node) : { children: [], status: 'loaded' as const };
@@ -129,6 +129,13 @@ export interface TreeRootProps<T> {
129
129
  */
130
130
  onActivate?: (node: TreeNode<T>, opts: TreeActivateOptions) => void;
131
131
 
132
+ /**
133
+ * Optional predicate. Nodes returning `false` (and their descendants) are
134
+ * not rendered and not searchable. Use this to hide dot-files, system
135
+ * entries, or anything else the consumer wants to filter out.
136
+ */
137
+ filterNode?: (node: TreeNode<T>) => boolean;
138
+
132
139
  /** Show built-in search input. Default: false. */
133
140
  enableSearch?: boolean;
134
141
  /** Type printable letters to jump to a matching name. Default: true. */