@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.
- package/dist/{DocsLayout-ESVQZO3V.mjs → DocsLayout-CTJINVBM.mjs} +235 -267
- package/dist/DocsLayout-CTJINVBM.mjs.map +1 -0
- package/dist/{DocsLayout-KUPDWJ3G.cjs → DocsLayout-XLDB6CJ2.cjs} +273 -305
- package/dist/DocsLayout-XLDB6CJ2.cjs.map +1 -0
- package/dist/TreeRoot-A3J65L6F.mjs +4 -0
- package/dist/{TreeRoot-R6XVHYQK.mjs.map → TreeRoot-A3J65L6F.mjs.map} +1 -1
- package/dist/TreeRoot-DSK5JILT.cjs +19 -0
- package/dist/{TreeRoot-RAMQSBMO.cjs.map → TreeRoot-DSK5JILT.cjs.map} +1 -1
- package/dist/{chunk-44ZTWYAF.cjs → chunk-3Z3A7FHA.cjs} +17 -6
- package/dist/chunk-3Z3A7FHA.cjs.map +1 -0
- package/dist/{chunk-GBLQTHWT.mjs → chunk-62Y65TGK.mjs} +5 -4
- package/dist/chunk-62Y65TGK.mjs.map +1 -0
- package/dist/{chunk-NTJL2SXK.mjs → chunk-MOME6KYD.mjs} +17 -6
- package/dist/chunk-MOME6KYD.mjs.map +1 -0
- package/dist/{chunk-S44PW6NK.cjs → chunk-TKSFZHCG.cjs} +5 -4
- package/dist/chunk-TKSFZHCG.cjs.map +1 -0
- package/dist/file-icon/index.cjs +59 -3
- package/dist/file-icon/index.cjs.map +1 -1
- package/dist/file-icon/index.d.cts +33 -4
- package/dist/file-icon/index.d.ts +33 -4
- package/dist/file-icon/index.mjs +60 -5
- package/dist/file-icon/index.mjs.map +1 -1
- package/dist/index.cjs +43 -43
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +6 -6
- package/dist/tree/index.cjs +33 -33
- package/dist/tree/index.d.cts +6 -4
- package/dist/tree/index.d.ts +6 -4
- package/dist/tree/index.mjs +1 -1
- package/dist/{types-Cclwv4Hl.d.cts → types-CevSbyfD.d.cts} +6 -0
- package/dist/{types-Cclwv4Hl.d.ts → types-CevSbyfD.d.ts} +6 -0
- package/package.json +6 -6
- package/src/tools/FileIcon/FileIcon.tsx +13 -2
- package/src/tools/FileIcon/index.ts +6 -0
- package/src/tools/FileIcon/specialFolders.ts +93 -0
- package/src/tools/FileIcon/treeAdapter.tsx +8 -0
- package/src/tools/OpenapiViewer/OpenapiViewer.story.tsx +30 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Header/MetaActions.tsx +35 -50
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Header/index.tsx +49 -22
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Section/index.tsx +1 -1
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/store/index.ts +10 -11
- package/src/tools/OpenapiViewer/components/DocsLayout/SchemaCopyMenu.tsx +25 -5
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/BrandHeader.tsx +18 -33
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/Toolbar.tsx +40 -24
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/index.tsx +8 -14
- package/src/tools/OpenapiViewer/components/DocsLayout/sidebarLabel.ts +1 -4
- package/src/tools/OpenapiViewer/utils/operationToHar.ts +2 -1
- package/src/tools/OpenapiViewer/utils/url.ts +9 -2
- package/src/tools/Tree/README.md +46 -2
- package/src/tools/Tree/Tree.story.tsx +36 -0
- package/src/tools/Tree/TreeRoot.tsx +2 -0
- package/src/tools/Tree/components/TreeContent.tsx +3 -1
- package/src/tools/Tree/context/TreeContext.tsx +4 -1
- package/src/tools/Tree/data/flatten.ts +10 -1
- package/src/tools/Tree/types.ts +7 -0
- package/dist/DocsLayout-ESVQZO3V.mjs.map +0 -1
- package/dist/DocsLayout-KUPDWJ3G.cjs.map +0 -1
- package/dist/TreeRoot-R6XVHYQK.mjs +0 -4
- package/dist/TreeRoot-RAMQSBMO.cjs +0 -19
- package/dist/chunk-44ZTWYAF.cjs.map +0 -1
- package/dist/chunk-GBLQTHWT.mjs.map +0 -1
- package/dist/chunk-NTJL2SXK.mjs.map +0 -1
- package/dist/chunk-S44PW6NK.cjs.map +0 -1
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/MethodChips.tsx +0 -43
package/src/tools/Tree/README.md
CHANGED
|
@@ -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.
|
|
268
|
-
|
|
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>({
|
|
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 };
|
package/src/tools/Tree/types.ts
CHANGED
|
@@ -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. */
|