@protobase/ui 0.12.6 → 0.12.8
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/app/app.js +4 -2
- package/dist/app/breadcrumb-for.js +8 -3
- package/dist/app/displays/cards-display.js +1 -1
- package/dist/app/displays/display-controls.js +2 -1
- package/dist/app/list-page.js +23 -14
- package/dist/app/list-search.js +1 -1
- package/dist/app/pages/blocks/card-row.js +1 -1
- package/dist/app/pages/blocks/field.js +1 -1
- package/dist/app/pages/blocks/record-card.js +1 -1
- package/dist/app/pages/blocks/stat.js +1 -1
- package/dist/app/pages/blocks/table.js +1 -1
- package/dist/app/related-section.js +1 -1
- package/dist/app-shell/breadcrumb.js +11 -4
- package/dist/data-grid/grid-row.js +1 -1
- package/dist/filter-panel/filter-panel.js +36 -28
- package/dist/filter-panel/toolbar-row.js +22 -0
- package/dist/package.js +1 -1
- package/dist/primitives/segmented.js +13 -3
- package/dist/shell/assets/app-BFCjH5PZ.js +65 -0
- package/dist/shell/assets/{index-BVOqIEYd.css → index-BMIEmNDF.css} +1 -1
- package/dist/shell/assets/{index--KkbQgIT.js → index-YPieq1OK.js} +1 -1
- package/dist/shell/assets/{shared-protobase-ui-DQxQC_dF.js → shared-protobase-ui-bPkMP70Y.js} +1 -1
- package/dist/shell/index.html +4 -4
- package/dist/shell/protobase.shell.json +1 -1
- package/package.json +4 -4
- package/dist/shell/assets/app-DRkDxaX2.js +0 -65
package/dist/app/app.js
CHANGED
|
@@ -86,7 +86,8 @@ const Routes = ({ workspace, sidebarMode, assistant, runtime }) => {
|
|
|
86
86
|
const group = groups.find((entry) => entry.items.some((item) => item.id === place));
|
|
87
87
|
const model = resource ? meta.resources[resource] : void 0;
|
|
88
88
|
const record = useRecord(shown?.resource ?? "", shown?.key ?? "", Boolean(model && shown));
|
|
89
|
-
const title = model && shown && record.data ? recordTitle(model, view, record.data.record, shown.key) :
|
|
89
|
+
const title = model && shown && record.data ? recordTitle(model, view, record.data.record, shown.key) : void 0;
|
|
90
|
+
const titleLoading = Boolean(shown) && (record.isLoading || drawnFrom?.state === "loading");
|
|
90
91
|
const { shell } = useProjectUi();
|
|
91
92
|
const Actions = shell?.actions;
|
|
92
93
|
const RightPanel = shell?.rightPanel;
|
|
@@ -126,7 +127,8 @@ const Routes = ({ workspace, sidebarMode, assistant, runtime }) => {
|
|
|
126
127
|
group: group?.label,
|
|
127
128
|
page,
|
|
128
129
|
resourceLabel: view?.names?.plural ?? (resource && humanize(resource)),
|
|
129
|
-
recordTitle: title
|
|
130
|
+
recordTitle: title,
|
|
131
|
+
recordLoading: titleLoading
|
|
130
132
|
});
|
|
131
133
|
const content = path === "/-/account" ? /* @__PURE__ */ jsx(AccountSecurityPage, { email: user.email }) : path === "/-/sign-in-policy" ? admin ? /* @__PURE__ */ jsx(SignInPolicyPage, {}) : /* @__PURE__ */ jsx(Notice, {
|
|
132
134
|
title: "Admins only",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { appPath } from "@protobase/schema";
|
|
2
2
|
//#region src/app/breadcrumb-for.ts
|
|
3
3
|
/** Group, then the page or resource list, then the record (or "New"): each with a page of its own links to it. */
|
|
4
|
-
const breadcrumbFor = ({ basePath, address, group, page, resourceLabel, recordTitle }) => {
|
|
4
|
+
const breadcrumbFor = ({ basePath, address, group, page, resourceLabel, recordTitle, recordLoading }) => {
|
|
5
5
|
const groupCrumb = group ? [group] : [];
|
|
6
6
|
if (page) return [...groupCrumb, {
|
|
7
7
|
label: page.title,
|
|
@@ -19,12 +19,17 @@ const breadcrumbFor = ({ basePath, address, group, page, resourceLabel, recordTi
|
|
|
19
19
|
}, basePath)
|
|
20
20
|
}] : [];
|
|
21
21
|
if (address.kind === "list") return [...groupCrumb, ...resource];
|
|
22
|
+
const href = appPath(address, basePath);
|
|
22
23
|
const record = address.kind === "create" ? {
|
|
23
24
|
label: "New",
|
|
24
|
-
href
|
|
25
|
+
href
|
|
26
|
+
} : recordTitle === void 0 && recordLoading ? {
|
|
27
|
+
label: "Loading record",
|
|
28
|
+
href,
|
|
29
|
+
loading: true
|
|
25
30
|
} : {
|
|
26
31
|
label: recordTitle ?? address.key,
|
|
27
|
-
href
|
|
32
|
+
href
|
|
28
33
|
};
|
|
29
34
|
return [
|
|
30
35
|
...groupCrumb,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Badge } from "../../primitives/badge.js";
|
|
2
|
+
import { Skeleton } from "../../primitives/skeleton.js";
|
|
2
3
|
import { humanize } from "../../live/naming.js";
|
|
3
4
|
import { useProjectUi } from "../pages/project-ui.js";
|
|
4
5
|
import { cardFields } from "./list-displays.js";
|
|
@@ -7,7 +8,6 @@ import { displayValue } from "../../live/display-value.js";
|
|
|
7
8
|
import { shownWidget } from "../field-types/shown-widget.js";
|
|
8
9
|
import { CustomBoundary } from "../pages/custom-boundary.js";
|
|
9
10
|
import { WidgetPart } from "../field-types/widget-part.js";
|
|
10
|
-
import { Skeleton } from "../../primitives/skeleton.js";
|
|
11
11
|
import { toneFor } from "../../live/columns-from-model.js";
|
|
12
12
|
import { DisplayFooter, DisplayState } from "./display-frame.js";
|
|
13
13
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -20,6 +20,7 @@ const DisplaySwitch = ({ view, value, onChange }) => {
|
|
|
20
20
|
if (choices.length < 2) return null;
|
|
21
21
|
return /* @__PURE__ */ jsx(Segmented, {
|
|
22
22
|
label: "Display",
|
|
23
|
+
size: "md",
|
|
23
24
|
value,
|
|
24
25
|
onChange,
|
|
25
26
|
options: choices.map((choice) => ({
|
|
@@ -40,7 +41,7 @@ const SortMenu = ({ model, view, display, orderBy, onChange }) => {
|
|
|
40
41
|
"aria-label": "Sort",
|
|
41
42
|
value: `${current.columnId} ${current.direction}`,
|
|
42
43
|
onChange: (event) => onChange(event.target.value),
|
|
43
|
-
className: "h-
|
|
44
|
+
className: "h-8 rounded-md border border-border-strong bg-background px-2.5 text-[13px] shadow-sm focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20",
|
|
44
45
|
children: names.flatMap((name) => directions.map(([direction, word]) => /* @__PURE__ */ jsxs("option", {
|
|
45
46
|
value: `${name} ${direction}`,
|
|
46
47
|
children: [
|
package/dist/app/list-page.js
CHANGED
|
@@ -15,6 +15,7 @@ import { pinnedStorageKey } from "../data-grid/pinned-storage.js";
|
|
|
15
15
|
import { DataGrid } from "../data-grid/data-grid.js";
|
|
16
16
|
import { ActiveFilterChips } from "../filter-panel/active-filter-chips.js";
|
|
17
17
|
import { activeChips, emptyFilterState, removeChip } from "../filter-panel/filter-state.js";
|
|
18
|
+
import { ToolbarRow } from "../filter-panel/toolbar-row.js";
|
|
18
19
|
import { FilterPanel } from "../filter-panel/filter-panel.js";
|
|
19
20
|
import { columnsFromModel } from "../live/columns-from-model.js";
|
|
20
21
|
import { stateToText, textToState } from "../live/filter-string.js";
|
|
@@ -124,11 +125,29 @@ const LoadedList = ({ model, address }) => {
|
|
|
124
125
|
setFilter(state, text ? [...others, where.search(text)] : others);
|
|
125
126
|
};
|
|
126
127
|
const plural = view?.names?.plural ?? humanize(resource);
|
|
128
|
+
const sortMenu = display && /* @__PURE__ */ jsx(SortMenu, {
|
|
129
|
+
model,
|
|
130
|
+
view,
|
|
131
|
+
display,
|
|
132
|
+
orderBy,
|
|
133
|
+
onChange: (next) => set({ orderBy: next })
|
|
134
|
+
});
|
|
135
|
+
const searchBox = model.search?.length ? /* @__PURE__ */ jsx(ListSearch, {
|
|
136
|
+
value: search?.kind === "search" ? search.text : "",
|
|
137
|
+
onCommit: commitSearch,
|
|
138
|
+
placeholder: `Search ${plural.toLowerCase()}`
|
|
139
|
+
}) : null;
|
|
127
140
|
const panel = /* @__PURE__ */ jsx(FilterPanel, {
|
|
128
141
|
layout,
|
|
129
142
|
config,
|
|
130
143
|
value: state,
|
|
131
|
-
onChange: setFilter
|
|
144
|
+
onChange: setFilter,
|
|
145
|
+
leading: sortMenu,
|
|
146
|
+
trailing: searchBox
|
|
147
|
+
});
|
|
148
|
+
const desktopToolbar = layout === "bar" ? panel : /* @__PURE__ */ jsx(ToolbarRow, {
|
|
149
|
+
groups: [sortMenu ? [sortMenu] : []],
|
|
150
|
+
trailing: searchBox
|
|
132
151
|
});
|
|
133
152
|
const chips = activeChips(config, state);
|
|
134
153
|
const phoneFilters = /* @__PURE__ */ jsxs("div", {
|
|
@@ -217,18 +236,8 @@ const LoadedList = ({ model, address }) => {
|
|
|
217
236
|
value: display?.name ?? tableDisplay,
|
|
218
237
|
onChange: (name) => set({ display: name })
|
|
219
238
|
}),
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
view,
|
|
223
|
-
display,
|
|
224
|
-
orderBy,
|
|
225
|
-
onChange: (next) => set({ orderBy: next })
|
|
226
|
-
}),
|
|
227
|
-
model.search?.length ? /* @__PURE__ */ jsx(ListSearch, {
|
|
228
|
-
value: search?.kind === "search" ? search.text : "",
|
|
229
|
-
onCommit: commitSearch,
|
|
230
|
-
placeholder: `Search ${plural.toLowerCase()}`
|
|
231
|
-
}) : null,
|
|
239
|
+
!desktop && sortMenu,
|
|
240
|
+
!desktop && searchBox,
|
|
232
241
|
can(permissions, "create") && /* @__PURE__ */ jsxs(Button, {
|
|
233
242
|
variant: "primary",
|
|
234
243
|
onClick: () => go({
|
|
@@ -258,7 +267,7 @@ const LoadedList = ({ model, address }) => {
|
|
|
258
267
|
noun: plural.toLowerCase(),
|
|
259
268
|
filter: parsed.ok ? filterText : ""
|
|
260
269
|
}),
|
|
261
|
-
|
|
270
|
+
desktop ? desktopToolbar : phoneFilters,
|
|
262
271
|
desktop && layout === "panel" ? /* @__PURE__ */ jsxs("div", {
|
|
263
272
|
className: "flex min-h-0 flex-1 gap-4",
|
|
264
273
|
children: [panel, shown]
|
package/dist/app/list-search.js
CHANGED
|
@@ -17,7 +17,7 @@ const ListSearch = ({ value, onCommit, placeholder }) => {
|
|
|
17
17
|
onCommit
|
|
18
18
|
]);
|
|
19
19
|
return /* @__PURE__ */ jsx(Input, {
|
|
20
|
-
wrapperClassName: "min-w-0 flex-1 sm:w-72 sm:flex-
|
|
20
|
+
wrapperClassName: "min-w-0 flex-1 sm:w-72 sm:flex-initial",
|
|
21
21
|
leading: /* @__PURE__ */ jsx(Search, { className: "size-4" }),
|
|
22
22
|
placeholder,
|
|
23
23
|
"aria-label": "Search",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { Skeleton } from "../../../primitives/skeleton.js";
|
|
1
2
|
import { useAdminMeta } from "../../meta-gate.js";
|
|
2
3
|
import { ErrorBanner } from "../../error-banner.js";
|
|
3
|
-
import { Skeleton } from "../../../primitives/skeleton.js";
|
|
4
4
|
import { RecordScope } from "../record-scope.js";
|
|
5
5
|
import { scopeOf, useListPage } from "../use-layout-data.js";
|
|
6
6
|
import { propsOf } from "./block-props.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { Skeleton } from "../../../primitives/skeleton.js";
|
|
1
2
|
import { humanize } from "../../../live/naming.js";
|
|
2
3
|
import { useAdminMeta } from "../../meta-gate.js";
|
|
3
|
-
import { Skeleton } from "../../../primitives/skeleton.js";
|
|
4
4
|
import { useLayoutRecord } from "../record-scope.js";
|
|
5
5
|
import { propsOf } from "./block-props.js";
|
|
6
6
|
import { FieldValue } from "../field-value.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { Skeleton } from "../../../primitives/skeleton.js";
|
|
1
2
|
import { useAdminMeta } from "../../meta-gate.js";
|
|
2
3
|
import { ErrorBanner } from "../../error-banner.js";
|
|
3
|
-
import { Skeleton } from "../../../primitives/skeleton.js";
|
|
4
4
|
import { RecordScope } from "../record-scope.js";
|
|
5
5
|
import { useFirstRecord } from "../use-layout-data.js";
|
|
6
6
|
import { propsOf } from "./block-props.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { Skeleton } from "../../../primitives/skeleton.js";
|
|
1
2
|
import { useAdminMeta } from "../../meta-gate.js";
|
|
2
3
|
import { AppLink } from "../../use-app-address.js";
|
|
3
|
-
import { Skeleton } from "../../../primitives/skeleton.js";
|
|
4
4
|
import { useLayoutRecord } from "../record-scope.js";
|
|
5
5
|
import { useCount } from "../use-layout-data.js";
|
|
6
6
|
import { propsOf } from "./block-props.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { Skeleton } from "../../../primitives/skeleton.js";
|
|
1
2
|
import { useAdminMeta } from "../../meta-gate.js";
|
|
2
3
|
import { ErrorBanner } from "../../error-banner.js";
|
|
3
|
-
import { Skeleton } from "../../../primitives/skeleton.js";
|
|
4
4
|
import { useListPage } from "../use-layout-data.js";
|
|
5
5
|
import { propsOf } from "./block-props.js";
|
|
6
6
|
import { CardFrame, Quiet } from "./card-frame.js";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { Skeleton } from "../primitives/skeleton.js";
|
|
1
2
|
import { keys } from "../data/query-keys.js";
|
|
2
3
|
import { useClient } from "../data/api-provider.js";
|
|
3
4
|
import { useAdminMeta } from "./meta-gate.js";
|
|
4
5
|
import { ErrorBanner } from "./error-banner.js";
|
|
5
6
|
import { Section } from "../record-view/section.js";
|
|
6
|
-
import { Skeleton } from "../primitives/skeleton.js";
|
|
7
7
|
import { RecordTable } from "./pages/record-table.js";
|
|
8
8
|
import { defaultColumns } from "./pages/blocks/table.js";
|
|
9
9
|
import { useState } from "react";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { Skeleton } from "../primitives/skeleton.js";
|
|
2
3
|
import { ChevronRight } from "lucide-react";
|
|
3
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
5
|
//#region src/app-shell/breadcrumb.tsx
|
|
@@ -9,26 +10,32 @@ const Breadcrumb = ({ parts, onNavigate }) => /* @__PURE__ */ jsx("nav", {
|
|
|
9
10
|
className: "flex min-w-0 items-center gap-1.5 text-[13px]",
|
|
10
11
|
children: parts.map((part, index) => {
|
|
11
12
|
const last = index === parts.length - 1;
|
|
12
|
-
const { label, href } = typeof part === "string" ? {
|
|
13
|
+
const { label, href, loading } = typeof part === "string" ? {
|
|
13
14
|
label: part,
|
|
14
|
-
href: void 0
|
|
15
|
+
href: void 0,
|
|
16
|
+
loading: false
|
|
15
17
|
} : part;
|
|
18
|
+
const content = loading ? /* @__PURE__ */ jsx(Skeleton, { className: "w-24" }) : label;
|
|
16
19
|
const text = cn("truncate", last ? "font-semibold" : "text-muted-foreground");
|
|
17
20
|
return /* @__PURE__ */ jsxs("span", {
|
|
18
21
|
className: cn("min-w-0 items-center gap-1.5", last ? "flex" : "hidden md:flex"),
|
|
19
22
|
children: [index > 0 && /* @__PURE__ */ jsx(ChevronRight, { className: "hidden size-3.5 shrink-0 text-faint-foreground md:block" }), href ? /* @__PURE__ */ jsx("a", {
|
|
20
23
|
href,
|
|
21
24
|
"aria-current": last ? "page" : void 0,
|
|
25
|
+
"aria-label": loading ? label : void 0,
|
|
26
|
+
"aria-busy": loading || void 0,
|
|
22
27
|
onClick: (event) => {
|
|
23
28
|
if (!onNavigate || !inApp(event)) return;
|
|
24
29
|
event.preventDefault();
|
|
25
30
|
onNavigate(href);
|
|
26
31
|
},
|
|
27
32
|
className: cn(text, "rounded-sm hover:text-foreground hover:underline"),
|
|
28
|
-
children:
|
|
33
|
+
children: content
|
|
29
34
|
}) : /* @__PURE__ */ jsx("span", {
|
|
30
35
|
className: text,
|
|
31
|
-
|
|
36
|
+
"aria-label": loading ? label : void 0,
|
|
37
|
+
"aria-busy": loading || void 0,
|
|
38
|
+
children: content
|
|
32
39
|
})]
|
|
33
40
|
}, `${index}-${label}`);
|
|
34
41
|
})
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { Skeleton } from "../primitives/skeleton.js";
|
|
2
3
|
import { formatInt } from "../format/number.js";
|
|
3
4
|
import { Checkbox } from "../primitives/checkbox.js";
|
|
4
5
|
import { gridTemplate } from "./grid-template.js";
|
|
5
6
|
import { isRightAligned } from "./column-spec.js";
|
|
6
|
-
import { Skeleton } from "../primitives/skeleton.js";
|
|
7
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
8
|
//#region src/data-grid/grid-row.tsx
|
|
9
9
|
const frameStyle = (frame) => ({
|
|
@@ -6,10 +6,12 @@ import { FilterMenu } from "./filter-menu.js";
|
|
|
6
6
|
import { FilterSection } from "./filter-section.js";
|
|
7
7
|
import { RangeFilter } from "./range-filter.js";
|
|
8
8
|
import { ToggleRow } from "./toggle-row.js";
|
|
9
|
+
import { ToolbarRow, toolbarShows } from "./toolbar-row.js";
|
|
9
10
|
import { useFilterState } from "./use-filter-state.js";
|
|
11
|
+
import { Fragment } from "react";
|
|
10
12
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
13
|
//#region src/filter-panel/filter-panel.tsx
|
|
12
|
-
const FilterPanel = ({ config, layout, value, defaultValue, onChange, lockedLabels, defaultOpenMenu, fullWidth }) => {
|
|
14
|
+
const FilterPanel = ({ config, layout, value, defaultValue, onChange, lockedLabels, defaultOpenMenu, fullWidth, leading, trailing }) => {
|
|
13
15
|
const [state, setState] = useFilterState(value, defaultValue, onChange);
|
|
14
16
|
const chips = activeChips(config, state);
|
|
15
17
|
const chipsView = /* @__PURE__ */ jsx(ActiveFilterChips, {
|
|
@@ -76,35 +78,41 @@ const FilterPanel = ({ config, layout, value, defaultValue, onChange, lockedLabe
|
|
|
76
78
|
})
|
|
77
79
|
]
|
|
78
80
|
});
|
|
81
|
+
const menus = [
|
|
82
|
+
...config.facets.map((group) => /* @__PURE__ */ jsx(FilterMenu, {
|
|
83
|
+
label: group.label,
|
|
84
|
+
activeCount: (state.facets[group.id] ?? []).length,
|
|
85
|
+
defaultOpen: defaultOpenMenu === group.id,
|
|
86
|
+
children: facetView(group)
|
|
87
|
+
}, group.id)),
|
|
88
|
+
...config.range ? [/* @__PURE__ */ jsx(FilterMenu, {
|
|
89
|
+
label: config.range.label,
|
|
90
|
+
activeCount: state.range ? 1 : 0,
|
|
91
|
+
width: 280,
|
|
92
|
+
defaultOpen: defaultOpenMenu === config.range.id,
|
|
93
|
+
children: rangeView
|
|
94
|
+
}, "range")] : [],
|
|
95
|
+
...dateView ? [/* @__PURE__ */ jsx(Fragment, { children: dateView }, "date")] : []
|
|
96
|
+
];
|
|
97
|
+
const toggles = (config.toggles ?? []).map((toggle) => /* @__PURE__ */ jsx("div", {
|
|
98
|
+
className: "flex items-center gap-2 text-[13px] text-muted-foreground",
|
|
99
|
+
children: /* @__PURE__ */ jsx(ToggleRow, {
|
|
100
|
+
label: toggle.label,
|
|
101
|
+
checked: state.toggles.includes(toggle.id),
|
|
102
|
+
onChange: () => setState(toggleFlag(state, toggle.id))
|
|
103
|
+
})
|
|
104
|
+
}, toggle.id));
|
|
105
|
+
const groups = [
|
|
106
|
+
leading ? [/* @__PURE__ */ jsx(Fragment, { children: leading }, "leading")] : [],
|
|
107
|
+
menus,
|
|
108
|
+
toggles
|
|
109
|
+
];
|
|
110
|
+
if (!toolbarShows(groups, trailing) && chips.length === 0 && !lockedLabels?.length) return null;
|
|
79
111
|
return /* @__PURE__ */ jsxs("div", {
|
|
80
112
|
className: "flex flex-col gap-2",
|
|
81
|
-
children: [/* @__PURE__ */
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
config.facets.map((group) => /* @__PURE__ */ jsx(FilterMenu, {
|
|
85
|
-
label: group.label,
|
|
86
|
-
activeCount: (state.facets[group.id] ?? []).length,
|
|
87
|
-
defaultOpen: defaultOpenMenu === group.id,
|
|
88
|
-
children: facetView(group)
|
|
89
|
-
}, group.id)),
|
|
90
|
-
config.range && /* @__PURE__ */ jsx(FilterMenu, {
|
|
91
|
-
label: config.range.label,
|
|
92
|
-
activeCount: state.range ? 1 : 0,
|
|
93
|
-
width: 280,
|
|
94
|
-
defaultOpen: defaultOpenMenu === config.range.id,
|
|
95
|
-
children: rangeView
|
|
96
|
-
}),
|
|
97
|
-
dateView,
|
|
98
|
-
config.toggles && /* @__PURE__ */ jsx("span", { className: "mx-1 h-5 w-px bg-border" }),
|
|
99
|
-
config.toggles?.map((toggle) => /* @__PURE__ */ jsx("div", {
|
|
100
|
-
className: "flex items-center gap-2 text-[13px] text-muted-foreground",
|
|
101
|
-
children: /* @__PURE__ */ jsx(ToggleRow, {
|
|
102
|
-
label: toggle.label,
|
|
103
|
-
checked: state.toggles.includes(toggle.id),
|
|
104
|
-
onChange: () => setState(toggleFlag(state, toggle.id))
|
|
105
|
-
})
|
|
106
|
-
}, toggle.id))
|
|
107
|
-
]
|
|
113
|
+
children: [/* @__PURE__ */ jsx(ToolbarRow, {
|
|
114
|
+
groups,
|
|
115
|
+
trailing
|
|
108
116
|
}), chipsView]
|
|
109
117
|
});
|
|
110
118
|
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Fragment } from "react";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
//#region src/filter-panel/toolbar-row.tsx
|
|
4
|
+
/** Whether a toolbar row with these groups and trailing control shows anything. */
|
|
5
|
+
const toolbarShows = (groups, trailing) => groups.some((group) => group.length > 0) || Boolean(trailing);
|
|
6
|
+
/**
|
|
7
|
+
* One row of list controls: the non-empty groups in order with a divider between each two, then the trailing control
|
|
8
|
+
* at the right end, narrowing before it wraps. Nothing at all when it would show nothing.
|
|
9
|
+
*/
|
|
10
|
+
const ToolbarRow = ({ groups, trailing }) => {
|
|
11
|
+
if (!toolbarShows(groups, trailing)) return null;
|
|
12
|
+
const shown = groups.filter((group) => group.length > 0);
|
|
13
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
14
|
+
className: "flex flex-wrap items-center gap-2",
|
|
15
|
+
children: [shown.map((group, index) => /* @__PURE__ */ jsxs(Fragment, { children: [index > 0 && /* @__PURE__ */ jsx("span", { className: "mx-1 h-5 w-px bg-border" }), group] }, index)), trailing && /* @__PURE__ */ jsx("div", {
|
|
16
|
+
className: "flex min-w-32 flex-1 justify-end",
|
|
17
|
+
children: trailing
|
|
18
|
+
})]
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
22
|
+
export { ToolbarRow, toolbarShows };
|
package/dist/package.js
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
//#region src/primitives/segmented.tsx
|
|
4
|
-
const
|
|
4
|
+
const sizes = {
|
|
5
|
+
sm: {
|
|
6
|
+
group: "h-7",
|
|
7
|
+
option: "px-2.5 text-xs"
|
|
8
|
+
},
|
|
9
|
+
md: {
|
|
10
|
+
group: "h-8",
|
|
11
|
+
option: "px-3 text-[13px]"
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
const Segmented = ({ value, options, onChange, label, size = "sm" }) => /* @__PURE__ */ jsx("span", {
|
|
5
15
|
role: "group",
|
|
6
16
|
"aria-label": label,
|
|
7
|
-
className: "inline-flex
|
|
17
|
+
className: cn("inline-flex", sizes[size].group, "rounded-md bg-muted p-0.5"),
|
|
8
18
|
children: options.map((option) => /* @__PURE__ */ jsxs("button", {
|
|
9
19
|
type: "button",
|
|
10
20
|
"aria-pressed": option.value === value,
|
|
11
21
|
onClick: () => onChange(option.value),
|
|
12
|
-
className: cn("inline-flex items-center gap-1.5 rounded
|
|
22
|
+
className: cn("inline-flex items-center gap-1.5 rounded", sizes[size].option, "font-medium transition-colors", option.value === value ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"),
|
|
13
23
|
children: [option.icon && /* @__PURE__ */ jsx(option.icon, { className: "size-3.5" }), option.label]
|
|
14
24
|
}, option.value))
|
|
15
25
|
});
|