@iloveagents/foundry-web-ui 0.18.0 → 0.19.0
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.
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Two bands, so a list with many filters still reads as ordered rather than
|
|
3
|
+
* as a ragged wrap:
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* row 1 search …………………………………… Filters (n) · actions · View · ⤢
|
|
6
|
+
* row 2 facet · facet · facet · + Filter · Reset
|
|
7
|
+
*
|
|
8
|
+
* Row one never wraps (its right group is fixed); filters live on their own
|
|
9
|
+
* row, and only the *pinned* facets (explicit `pinned`, else the first
|
|
10
|
+
* `pinnedFacets`) sit there — the rest wait behind "+ Filter" and join once
|
|
11
|
+
* chosen or active. On a dense, Excel-like screen the filter row collapses
|
|
12
|
+
* behind the "Filters" toggle, which keeps the active count visible.
|
|
13
|
+
*
|
|
14
|
+
* `layout="inline"` puts everything back on one wrapping row (a small list
|
|
15
|
+
* with one or two facets and no actions).
|
|
9
16
|
*/
|
|
10
17
|
import type { Table } from "@tanstack/react-table";
|
|
11
18
|
import { type ReactNode } from "react";
|
|
@@ -17,9 +24,19 @@ export interface DataTableFacet {
|
|
|
17
24
|
/** Always show this facet; unset → the first `pinnedFacets` are pinned. */
|
|
18
25
|
pinned?: boolean;
|
|
19
26
|
}
|
|
27
|
+
export type DataTableToolbarLayout = "auto" | "inline" | "stacked";
|
|
20
28
|
export interface DataTableToolbarProps<T> {
|
|
21
29
|
table: Table<T>;
|
|
22
30
|
facets?: DataTableFacet[];
|
|
31
|
+
/**
|
|
32
|
+
* "auto" (default): filters get their own row as soon as there are facets;
|
|
33
|
+
* "stacked": always; "inline": one wrapping row.
|
|
34
|
+
*/
|
|
35
|
+
layout?: DataTableToolbarLayout;
|
|
36
|
+
/** Show the Filters toggle that hides the filter row (default true when stacked). */
|
|
37
|
+
collapsibleFilters?: boolean;
|
|
38
|
+
/** Whether the filter row starts open (default `true`; when closed the toggle still shows the active count). */
|
|
39
|
+
defaultFiltersOpen?: boolean;
|
|
23
40
|
/** Facets always visible when none declares `pinned` (default 3; `Infinity` shows all). */
|
|
24
41
|
pinnedFacets?: number;
|
|
25
42
|
searchPlaceholder?: string;
|
|
@@ -40,4 +57,4 @@ export declare function visibleFacets<T>(table: Table<T>, facets: DataTableFacet
|
|
|
40
57
|
shown: DataTableFacet[];
|
|
41
58
|
hidden: DataTableFacet[];
|
|
42
59
|
};
|
|
43
|
-
export declare function DataTableToolbar<T>({ table, facets, pinnedFacets, searchPlaceholder, search, viewOptions, focus, children, actions, className, }: DataTableToolbarProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
60
|
+
export declare function DataTableToolbar<T>({ table, facets, layout, collapsibleFilters, defaultFiltersOpen, pinnedFacets, searchPlaceholder, search, viewOptions, focus, children, actions, className, }: DataTableToolbarProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Maximize2, Minimize2, Plus, Search, X } from "lucide-react";
|
|
2
|
+
import { Maximize2, Minimize2, Plus, Search, SlidersHorizontal, X } from "lucide-react";
|
|
3
3
|
import { useState } from "react";
|
|
4
4
|
import { Button, Input, cn } from "@iloveagents/foundry-web-primitives";
|
|
5
5
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "../../ui/dropdown-menu.js";
|
|
@@ -22,31 +22,47 @@ export function visibleFacets(table, facets, revealed, pinnedFacets) {
|
|
|
22
22
|
});
|
|
23
23
|
return { shown, hidden };
|
|
24
24
|
}
|
|
25
|
-
export function DataTableToolbar({ table, facets = [], pinnedFacets = 3, searchPlaceholder = "Search…", search = true, viewOptions = true, focus = true, children, actions, className, }) {
|
|
25
|
+
export function DataTableToolbar({ table, facets = [], layout = "auto", collapsibleFilters, defaultFiltersOpen = true, pinnedFacets = 3, searchPlaceholder = "Search…", search = true, viewOptions = true, focus = true, children, actions, className, }) {
|
|
26
26
|
const [revealed, setRevealed] = useState(() => new Set());
|
|
27
|
+
const [filtersOpen, setFiltersOpen] = useState(defaultFiltersOpen);
|
|
27
28
|
const frame = useDataTableFrame();
|
|
28
29
|
const focusButton = focus && frame ? (_jsx(Button, { type: "button", variant: "outline", size: "sm", "aria-pressed": frame.focused, "aria-label": frame.focused ? "Leave focus mode" : "Focus on the list", title: frame.focused ? "Leave focus mode (Esc)" : "Focus on the list", onClick: () => frame.setFocused(!frame.focused), className: "size-8 rounded-xl border-border/45 bg-background/65 p-0 shadow-none hover:bg-muted/26", children: frame.focused ? _jsx(Minimize2, { className: "size-4" }) : _jsx(Maximize2, { className: "size-4" }) })) : null;
|
|
29
30
|
const state = table.getState();
|
|
30
31
|
const isFiltered = state.columnFilters.length > 0 || Boolean(state.globalFilter) || revealed.size > 0;
|
|
31
32
|
const { shown, hidden } = visibleFacets(table, facets, revealed, pinnedFacets);
|
|
33
|
+
// `children` alone (extra controls, no facets) is not a reason to open a
|
|
34
|
+
// second band — it rides in the filter row only when there is one.
|
|
35
|
+
const stacked = layout === "stacked" || (layout === "auto" && facets.length > 0);
|
|
36
|
+
const canCollapse = stacked && (collapsibleFilters ?? true);
|
|
37
|
+
const activeFilters = state.columnFilters.length;
|
|
38
|
+
const showFilters = !canCollapse || filtersOpen;
|
|
32
39
|
const facetTitle = (facet) => {
|
|
33
40
|
const column = table.getColumn(facet.columnId);
|
|
34
41
|
return facet.title ?? (column ? columnLabel(column) : facet.columnId);
|
|
35
42
|
};
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
43
|
+
const searchNode = search ? (_jsxs("div", { className: "relative", children: [_jsx(Search, { className: "pointer-events-none absolute left-2.5 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { type: "search", value: state.globalFilter ?? "", onChange: (event) => table.setGlobalFilter(event.target.value), placeholder: searchPlaceholder, "aria-label": searchPlaceholder, className: "h-8 w-[160px] rounded-xl border-border/45 bg-background/72 pl-8 text-sm shadow-none lg:w-[250px]" })] })) : null;
|
|
44
|
+
const facetNodes = shown.map((facet) => {
|
|
45
|
+
const column = table.getColumn(facet.columnId);
|
|
46
|
+
if (!column)
|
|
47
|
+
return null;
|
|
48
|
+
return (_jsx(DataTableFacetedFilter, { column: column, title: facet.title, options: facet.options, onEmptyClose: revealed.has(facet.columnId)
|
|
49
|
+
? () => setRevealed((current) => {
|
|
50
|
+
const next = new Set(current);
|
|
51
|
+
next.delete(facet.columnId);
|
|
52
|
+
return next;
|
|
53
|
+
})
|
|
54
|
+
: undefined }, facet.columnId));
|
|
55
|
+
});
|
|
56
|
+
const addFilterNode = hidden.length > 0 ? (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { type: "button", variant: "ghost", size: "sm", "data-facet-menu": "", className: "h-8 rounded-xl px-2.5 text-muted-foreground hover:text-foreground", children: [_jsx(Plus, { className: "size-4" }), "Filter"] }) }), _jsxs(DropdownMenuContent, { align: "start", className: "w-48 rounded-xl border-border/45 bg-popover/95 shadow-lg", children: [_jsx(DropdownMenuLabel, { children: "Filter by" }), _jsx(DropdownMenuSeparator, {}), hidden.map((facet) => (_jsx(DropdownMenuItem, { "data-facet-menu-item": facet.columnId, onSelect: () => setRevealed((current) => new Set([...current, facet.columnId])), children: facetTitle(facet) }, facet.columnId)))] })] })) : null;
|
|
57
|
+
const resetNode = isFiltered ? (_jsxs(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => {
|
|
58
|
+
table.resetColumnFilters();
|
|
59
|
+
table.setGlobalFilter("");
|
|
60
|
+
setRevealed(new Set());
|
|
61
|
+
}, className: "h-8 rounded-xl px-2.5", children: ["Reset", _jsx(X, { className: "size-4" })] })) : null;
|
|
62
|
+
const filtersToggle = canCollapse ? (_jsxs(Button, { type: "button", variant: "outline", size: "sm", "data-filters-toggle": "", "aria-expanded": showFilters, onClick: () => setFiltersOpen((current) => !current), className: cn("h-8 rounded-xl border-border/45 bg-background/65 shadow-none hover:bg-muted/26", activeFilters > 0 && "border-primary/30"), children: [_jsx(SlidersHorizontal, { className: "size-4" }), "Filters", activeFilters > 0 ? (_jsxs("span", { className: "rounded-md bg-muted px-1.5 py-0.5 text-xs font-normal tabular-nums", children: [_jsx("span", { "aria-hidden": "true", children: activeFilters }), _jsxs("span", { className: "sr-only", children: [activeFilters, " active"] })] })) : null] })) : null;
|
|
63
|
+
const rightGroup = actions || viewOptions || focusButton || filtersToggle ? (_jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [filtersToggle, actions, viewOptions ? _jsx(DataTableViewOptions, { table: table }) : null, focusButton] })) : null;
|
|
64
|
+
if (!stacked) {
|
|
65
|
+
return (_jsxs("div", { className: cn("flex items-start gap-2", className), children: [_jsxs("div", { className: "flex min-w-0 flex-1 flex-wrap items-center gap-2", children: [searchNode, facetNodes, addFilterNode, children, resetNode] }), rightGroup ? _jsx("div", { className: "ml-auto", children: rightGroup }) : null] }));
|
|
66
|
+
}
|
|
67
|
+
return (_jsxs("div", { className: cn("flex flex-col gap-2", className), children: [_jsxs("div", { className: "flex items-center gap-2", children: [searchNode, rightGroup ? _jsx("div", { className: "ml-auto", children: rightGroup }) : null] }), showFilters ? (_jsxs("div", { "data-filter-row": "", className: "flex flex-wrap items-center gap-2", children: [facetNodes, addFilterNode, children, resetNode] })) : null] }));
|
|
52
68
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -35,7 +35,7 @@ export { InfiniteScrollSentinel } from "./components/infinite-scroll-sentinel.js
|
|
|
35
35
|
export { CollectionSurface } from "./components/collection-surface.js";
|
|
36
36
|
export { DataTable, type DataTableProps } from "./components/data-table/data-table.js";
|
|
37
37
|
export { DataTableFrame, useDataTableFrame, type DataTableFrameProps, type DataTableFrameContextValue, } from "./components/data-table/data-table-frame.js";
|
|
38
|
-
export { DataTableToolbar, visibleFacets, type DataTableFacet, type DataTableToolbarProps, } from "./components/data-table/data-table-toolbar.js";
|
|
38
|
+
export { DataTableToolbar, visibleFacets, type DataTableFacet, type DataTableToolbarLayout, type DataTableToolbarProps, } from "./components/data-table/data-table-toolbar.js";
|
|
39
39
|
export { DataTableFacetedFilter, type DataTableFacetedFilterProps, } from "./components/data-table/data-table-faceted-filter.js";
|
|
40
40
|
export { DataTableViewOptions, type DataTableViewOptionsProps, } from "./components/data-table/data-table-view-options.js";
|
|
41
41
|
export { DataTableColumnHeader, type DataTableColumnHeaderProps, } from "./components/data-table/data-table-column-header.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iloveagents/foundry-web-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "React agent UI core for Foundry UI — chat, composer, AG-UI adapter for assistant-ui, tool cards, panels, sidebar, theme runtime, and UI stores.",
|
|
6
6
|
"keywords": [
|
|
@@ -71,8 +71,8 @@
|
|
|
71
71
|
"react-markdown": "^10.0.0",
|
|
72
72
|
"remark-gfm": "^4.0.0",
|
|
73
73
|
"tailwind-merge": "^3.5.0",
|
|
74
|
-
"@iloveagents/foundry-agent": "^0.
|
|
75
|
-
"@iloveagents/foundry-web-primitives": "^0.
|
|
74
|
+
"@iloveagents/foundry-agent": "^0.19.0",
|
|
75
|
+
"@iloveagents/foundry-web-primitives": "^0.19.0"
|
|
76
76
|
},
|
|
77
77
|
"devDependencies": {
|
|
78
78
|
"@ag-ui/client": "^0.0.52",
|