@columenlabs/lumen-ui 0.1.63 → 0.1.65
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/index.d.ts +1 -1
- package/dist/settings.d.ts +10 -3
- package/dist/settings.js +17 -9
- package/dist/shell.d.ts +4 -2
- package/dist/shell.js +7 -4
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -54,7 +54,7 @@ export { LumenPageState } from './lumen-page-state.js';
|
|
|
54
54
|
export { type FormIssue, goToIssue, SaveButton } from './form-issues.js';
|
|
55
55
|
export { LumenForm, type LumenFormField, lumenFormIssues, type LumenFormValue, type LumenFormValues, } from './lumen-form.js';
|
|
56
56
|
export { RouteError, isStaleBuildError, reloadForNewBuild, reportError, watchNewBuilds, } from './route-error.js';
|
|
57
|
-
export { LumenSettings, SettingRow, SettingsActions, SettingsFooter, type SettingsSectionDef, } from './settings.js';
|
|
57
|
+
export { LumenSettings, SettingRow, SettingsActions, SettingsFooter, type SettingsGroupDef, type SettingsSectionDef, } from './settings.js';
|
|
58
58
|
export { LumenCard } from './summary-cards.js';
|
|
59
59
|
export { WhatsNew } from './whats-new.js';
|
|
60
60
|
export { type LumenApp, LumenAppSwitcher } from './app-switcher.js';
|
package/dist/settings.d.ts
CHANGED
|
@@ -13,6 +13,11 @@ export interface SettingsSectionDef {
|
|
|
13
13
|
* every section, empty when it has nothing; `SettingsFooter` fills it too. */
|
|
14
14
|
footer?: ReactNode;
|
|
15
15
|
}
|
|
16
|
+
/** A labelled group of sections in Settings: one per app when a product shows several apps' settings together. */
|
|
17
|
+
export interface SettingsGroupDef {
|
|
18
|
+
label: string;
|
|
19
|
+
sections: SettingsSectionDef[];
|
|
20
|
+
}
|
|
16
21
|
/** Renders its children in the section header, right of the title. */
|
|
17
22
|
export declare function SettingsActions({ children }: {
|
|
18
23
|
children: ReactNode;
|
|
@@ -32,9 +37,9 @@ export declare function SettingRow({ label, description, htmlFor, children, }: {
|
|
|
32
37
|
export declare function ThemeToggle({ compact }: {
|
|
33
38
|
compact?: boolean;
|
|
34
39
|
}): import("react").JSX.Element;
|
|
35
|
-
/** Every Settings section in the order the dialog lists them: General, the product's, the app's. */
|
|
36
|
-
export declare function allSettings(profile: ReactNode | undefined, shared?: SettingsSectionDef[], sections?: SettingsSectionDef[], security?: ReactNode): SettingsSectionDef[];
|
|
37
|
-
export declare function LumenSettings({ open, section, onOpen, onClose, appLabel, profile, security, sections, shared, sharedLabel, }: {
|
|
40
|
+
/** Every Settings section in the order the dialog lists them: General, the product's, the groups', the app's. */
|
|
41
|
+
export declare function allSettings(profile: ReactNode | undefined, shared?: SettingsSectionDef[], sections?: SettingsSectionDef[], security?: ReactNode, groups?: SettingsGroupDef[]): SettingsSectionDef[];
|
|
42
|
+
export declare function LumenSettings({ open, section, onOpen, onClose, appLabel, profile, security, sections, shared, sharedLabel, groups, }: {
|
|
38
43
|
open: boolean;
|
|
39
44
|
/** The section shown; unknown ids show Profile. */
|
|
40
45
|
section: string;
|
|
@@ -50,4 +55,6 @@ export declare function LumenSettings({ open, section, onOpen, onClose, appLabel
|
|
|
50
55
|
/** Sections the product adds to every app (the assistant), listed under `sharedLabel` after General. */
|
|
51
56
|
shared?: SettingsSectionDef[];
|
|
52
57
|
sharedLabel?: string;
|
|
58
|
+
/** More labelled groups (one per app), listed after the shared ones and before the app's own. */
|
|
59
|
+
groups?: SettingsGroupDef[];
|
|
53
60
|
}): import("react").JSX.Element;
|
package/dist/settings.js
CHANGED
|
@@ -101,18 +101,25 @@ const GENERAL = [
|
|
|
101
101
|
content: _jsx(Preferences, {}),
|
|
102
102
|
},
|
|
103
103
|
];
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const general = [
|
|
104
|
+
function generalSettings(profile, security) {
|
|
105
|
+
return [
|
|
107
106
|
...(profile ? [{ ...PROFILE, content: profile }] : []),
|
|
108
107
|
...(security ? [{ ...SECURITY, content: security }] : []),
|
|
109
108
|
...GENERAL,
|
|
110
109
|
];
|
|
111
|
-
return [...general, ...shared, ...sections];
|
|
112
110
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
111
|
+
/** Every Settings section in the order the dialog lists them: General, the product's, the groups', the app's. */
|
|
112
|
+
export function allSettings(profile, shared = [], sections = [], security, groups = []) {
|
|
113
|
+
return [
|
|
114
|
+
...generalSettings(profile, security),
|
|
115
|
+
...shared,
|
|
116
|
+
...groups.flatMap((g) => g.sections),
|
|
117
|
+
...sections,
|
|
118
|
+
];
|
|
119
|
+
}
|
|
120
|
+
export function LumenSettings({ open, section, onOpen, onClose, appLabel, profile, security, sections = [], shared = [], sharedLabel = 'All apps', groups = [], }) {
|
|
121
|
+
const all = allSettings(profile, shared, sections, security, groups);
|
|
122
|
+
const general = generalSettings(profile, security);
|
|
116
123
|
const current = all.find((s) => s.id === section) ?? all[0];
|
|
117
124
|
// The assistant opens Settings at a section (it never changes a setting); Undo puts it back as it was.
|
|
118
125
|
useAssistantMoves('settings', {
|
|
@@ -151,9 +158,10 @@ export function LumenSettings({ open, section, onOpen, onClose, appLabel, profil
|
|
|
151
158
|
setQuery('');
|
|
152
159
|
onOpen(id);
|
|
153
160
|
};
|
|
154
|
-
const
|
|
161
|
+
const navGroups = [
|
|
155
162
|
{ label: 'General', items: general.filter(shown) },
|
|
156
163
|
{ label: sharedLabel, items: shared.filter(shown) },
|
|
164
|
+
...groups.map((g) => ({ label: g.label, items: g.sections.filter(shown) })),
|
|
157
165
|
...(sections.length ? [{ label: appLabel, items: sections.filter(shown) }] : []),
|
|
158
166
|
].filter((g) => g.items.length);
|
|
159
167
|
const search = (_jsxs(InputGroup, { className: "bg-background", children: [_jsx(InputGroupAddon, { children: _jsx(SearchIcon, {}) }), _jsx(InputGroupInput, { type: "search", className: "[&::-webkit-search-cancel-button]:hidden", "aria-label": "Search settings", placeholder: "Search", value: query, onChange: (e) => setQuery(e.target.value) }), query && (_jsx(InputGroupAddon, { align: "inline-end", children: _jsx(InputGroupButton, { size: "icon-xs", "aria-label": "Clear search", onClick: () => setQuery(''), children: _jsx(XIcon, {}) }) }))] }));
|
|
@@ -172,7 +180,7 @@ export function LumenSettings({ open, section, onOpen, onClose, appLabel, profil
|
|
|
172
180
|
e.preventDefault();
|
|
173
181
|
setQuery('');
|
|
174
182
|
}
|
|
175
|
-
}, className: "flex h-[min(720px,calc(100svh-2rem))] flex-col gap-0 overflow-hidden p-0 sm:max-w-4xl md:flex-row", children: [_jsx(DialogTitle, { className: "sr-only", children: "Settings" }), _jsx(DialogDescription, { className: "sr-only", children: "Your preferences and this desk's settings." }), _jsxs("nav", { "aria-label": "Settings sections", className: "hidden w-56 shrink-0 flex-col border-r bg-sidebar py-2 md:flex", children: [_jsx("div", { className: "px-2 pt-1 pb-1", children: search }),
|
|
183
|
+
}, className: "flex h-[min(720px,calc(100svh-2rem))] flex-col gap-0 overflow-hidden p-0 sm:max-w-4xl md:flex-row", children: [_jsx(DialogTitle, { className: "sr-only", children: "Settings" }), _jsx(DialogDescription, { className: "sr-only", children: "Your preferences and this desk's settings." }), _jsxs("nav", { "aria-label": "Settings sections", className: "hidden w-56 shrink-0 flex-col border-r bg-sidebar py-2 md:flex", children: [_jsx("div", { className: "px-2 pt-1 pb-1", children: search }), navGroups.map((g) => (_jsxs(SidebarGroup, { className: "py-1", children: [_jsx(SidebarGroupLabel, { children: g.label }), _jsx(SidebarGroupContent, { children: _jsx(SidebarMenu, { children: g.items.map((s) => (_jsx(SidebarMenuItem, { children: _jsxs(SidebarMenuButton, { isActive: !q && s.id === current.id, className: s.sub ? 'pl-7' : undefined, onClick: () => go(s.id), children: [_jsx(s.icon, {}), _jsx("span", { children: s.title })] }) }, s.id))) }) })] }, g.label)))] }), _jsxs("div", { className: "flex flex-col gap-2 border-b p-3 pr-12 md:hidden", children: [search, !q && (_jsx(LumenSelector, { label: "Settings section", className: "w-full", groups: navGroups.map((g) => ({
|
|
176
184
|
label: g.label,
|
|
177
185
|
options: g.items.map((s) => ({
|
|
178
186
|
value: s.id,
|
package/dist/shell.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import { type LumenFinderConfig } from './lumen-finder.js';
|
|
3
|
-
import { type SettingsSectionDef } from './settings.js';
|
|
3
|
+
import { type SettingsGroupDef, type SettingsSectionDef } from './settings.js';
|
|
4
4
|
interface ShellContext {
|
|
5
5
|
/** The app this page renders in (`data-app` on the root `<html>`). */
|
|
6
6
|
app: string;
|
|
@@ -65,6 +65,8 @@ export interface LumenShellProps {
|
|
|
65
65
|
/** Sections every app of the product shows, listed after General under `sharedLabel`. */
|
|
66
66
|
shared?: SettingsSectionDef[];
|
|
67
67
|
sharedLabel?: string;
|
|
68
|
+
/** More labelled Settings groups (one per app of a product), listed after `shared`, before the app's own. */
|
|
69
|
+
settingsGroups?: SettingsGroupDef[];
|
|
68
70
|
/** Who is signed in (the profile button); null while it loads. */
|
|
69
71
|
user?: ShellUser | null;
|
|
70
72
|
/** Where Sign out goes; no Sign out without it. */
|
|
@@ -91,7 +93,7 @@ export interface LumenShellProps {
|
|
|
91
93
|
/** The routed page (usually `<Outlet />`). */
|
|
92
94
|
children: ReactNode;
|
|
93
95
|
}
|
|
94
|
-
export declare function LumenShell({ app, appLabel, brand, nav, status, health, statusEnd, releases, releaseApps, settings, shared, sharedLabel, user, signOutUrl, profile, security, onRefresh, hideAmounts, onLogs, headerActions, panel, finder, wrap, children, }: LumenShellProps): import("react").JSX.Element;
|
|
96
|
+
export declare function LumenShell({ app, appLabel, brand, nav, status, health, statusEnd, releases, releaseApps, settings, shared, sharedLabel, settingsGroups, user, signOutUrl, profile, security, onRefresh, hideAmounts, onLogs, headerActions, panel, finder, wrap, children, }: LumenShellProps): import("react").JSX.Element;
|
|
95
97
|
/** A status bar entry: an optional health dot, a label and a mono value. */
|
|
96
98
|
export declare function StatusItem({ label, value, ok, className, }: {
|
|
97
99
|
label: ReactNode;
|
package/dist/shell.js
CHANGED
|
@@ -11,7 +11,7 @@ import { cn } from './lib/utils.js';
|
|
|
11
11
|
import { LumenFinder } from './lumen-finder.js';
|
|
12
12
|
import { usePrefs } from './prefs.js';
|
|
13
13
|
import { withRefreshToken } from './refresh.js';
|
|
14
|
-
import { LumenSettings, ThemeToggle, allSettings } from './settings.js';
|
|
14
|
+
import { LumenSettings, ThemeToggle, allSettings, } from './settings.js';
|
|
15
15
|
import { SourceHealthProvider } from './source-health.js';
|
|
16
16
|
import { StatusTimeZone } from './status.js';
|
|
17
17
|
import { Avatar, AvatarFallback } from './ui/avatar.js';
|
|
@@ -88,7 +88,7 @@ const settingsFromUrl = (() => {
|
|
|
88
88
|
window.history.replaceState(window.history.state, '', url);
|
|
89
89
|
return at || 'profile';
|
|
90
90
|
})();
|
|
91
|
-
export function LumenShell({ app, appLabel, brand, nav, status, health, statusEnd, releases, releaseApps, settings, shared, sharedLabel, user = null, signOutUrl, profile, security, onRefresh, hideAmounts = false, onLogs, headerActions = null, panel, finder, wrap = (frame) => frame, children, }) {
|
|
91
|
+
export function LumenShell({ app, appLabel, brand, nav, status, health, statusEnd, releases, releaseApps, settings, shared, sharedLabel, settingsGroups, user = null, signOutUrl, profile, security, onRefresh, hideAmounts = false, onLogs, headerActions = null, panel, finder, wrap = (frame) => frame, children, }) {
|
|
92
92
|
const [statusSlot, setStatusSlot] = useState(null);
|
|
93
93
|
const [refreshing, setRefreshing] = useState(false);
|
|
94
94
|
// Refresh reloads the layout too: once the data is fresh the page mounts
|
|
@@ -117,7 +117,10 @@ export function LumenShell({ app, appLabel, brand, nav, status, health, statusEn
|
|
|
117
117
|
document.documentElement.dataset.app = app;
|
|
118
118
|
}, [app]);
|
|
119
119
|
// Find lists every Settings section by name.
|
|
120
|
-
const sectionNames = JSON.stringify(allSettings(profile, shared, settings, security).map((s) => ({
|
|
120
|
+
const sectionNames = JSON.stringify(allSettings(profile, shared, settings, security, settingsGroups).map((s) => ({
|
|
121
|
+
id: s.id,
|
|
122
|
+
title: s.title,
|
|
123
|
+
})));
|
|
121
124
|
const findSettings = useMemo(() => JSON.parse(sectionNames), [sectionNames]);
|
|
122
125
|
return (_jsx(Ctx.Provider, { value: {
|
|
123
126
|
app,
|
|
@@ -129,7 +132,7 @@ export function LumenShell({ app, appLabel, brand, nav, status, health, statusEn
|
|
|
129
132
|
refreshing,
|
|
130
133
|
hideAmounts,
|
|
131
134
|
headerActions,
|
|
132
|
-
}, children: _jsx(SourceHealthProvider, { children: wrap(_jsx(TooltipProvider, { delayDuration: 200, children: _jsx(SidebarProvider, { defaultOpen: sidebarOpen(), children: _jsxs(LumenFinder, { app: app, settings: findSettings, openSettings: openSettings, config: finder, children: [_jsx(CloseDrawerOnNavigate, {}), _jsxs(Sidebar, { collapsible: "icon", children: [_jsx(SidebarHeader, { children: brand }), _jsx(SidebarContent, { children: nav }), _jsx(SidebarFooter, { children: _jsx(NavUser, { who: user, signOutUrl: signOutUrl, onSettings: () => openSettings(), onLogs: onLogs }) }), _jsx(SidebarRail, {})] }), _jsxs(SidebarInset, { className: "flex min-h-svh min-w-0 flex-col", children: [_jsx("div", { className: "flex min-w-0 flex-1 flex-col", children: children }, epoch), _jsxs("footer", { "data-slot": "shell-status", "aria-label": "Status", className: "sticky bottom-0 z-10 flex h-(--shell-status-height) shrink-0 items-center gap-4 overflow-x-auto overflow-y-hidden border-t bg-background px-(--shell-gutter) text-xs whitespace-nowrap text-muted-foreground", children: [status, _jsx("div", { ref: setStatusSlot, className: "flex shrink-0 items-center gap-4" }), _jsxs("span", { className: "ml-auto flex shrink-0 items-center gap-1", children: [health, releases && _jsx(WhatsNew, { api: releases, apps: releaseApps }), statusEnd, _jsx(StatusTimeZone, {})] })] })] }), panel, _jsx(LumenSettings, { open: settingsAt !== null, section: settingsAt ?? lastSection, onOpen: openSettings, onClose: () => setSettingsAt(null), appLabel: appLabel, profile: profile, security: security, sections: settings, shared: shared, sharedLabel: sharedLabel }), _jsx(Toaster, { position: "bottom-right" })] }) }) })) }) }));
|
|
135
|
+
}, children: _jsx(SourceHealthProvider, { children: wrap(_jsx(TooltipProvider, { delayDuration: 200, children: _jsx(SidebarProvider, { defaultOpen: sidebarOpen(), children: _jsxs(LumenFinder, { app: app, settings: findSettings, openSettings: openSettings, config: finder, children: [_jsx(CloseDrawerOnNavigate, {}), _jsxs(Sidebar, { collapsible: "icon", children: [_jsx(SidebarHeader, { children: brand }), _jsx(SidebarContent, { children: nav }), _jsx(SidebarFooter, { children: _jsx(NavUser, { who: user, signOutUrl: signOutUrl, onSettings: () => openSettings(), onLogs: onLogs }) }), _jsx(SidebarRail, {})] }), _jsxs(SidebarInset, { className: "flex min-h-svh min-w-0 flex-col", children: [_jsx("div", { className: "flex min-w-0 flex-1 flex-col", children: children }, epoch), _jsxs("footer", { "data-slot": "shell-status", "aria-label": "Status", className: "sticky bottom-0 z-10 flex h-(--shell-status-height) shrink-0 items-center gap-4 overflow-x-auto overflow-y-hidden border-t bg-background px-(--shell-gutter) text-xs whitespace-nowrap text-muted-foreground", children: [status, _jsx("div", { ref: setStatusSlot, className: "flex shrink-0 items-center gap-4" }), _jsxs("span", { className: "ml-auto flex shrink-0 items-center gap-1", children: [health, releases && _jsx(WhatsNew, { api: releases, apps: releaseApps }), statusEnd, _jsx(StatusTimeZone, {})] })] })] }), panel, _jsx(LumenSettings, { open: settingsAt !== null, section: settingsAt ?? lastSection, onOpen: openSettings, onClose: () => setSettingsAt(null), appLabel: appLabel, profile: profile, security: security, sections: settings, shared: shared, sharedLabel: sharedLabel, groups: settingsGroups }), _jsx(Toaster, { position: "bottom-right" })] }) }) })) }) }));
|
|
133
136
|
}
|
|
134
137
|
/** A status bar entry: an optional health dot, a label and a mono value. */
|
|
135
138
|
export function StatusItem({ label, value, ok, className, }) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@columenlabs/lumen-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.65",
|
|
4
4
|
"description": "Lumen: shared React components (frame, views, widgets, charts, drawers).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"@codemirror/lang-javascript": "6.2.5",
|
|
42
42
|
"@codemirror/lang-json": "6.0.2",
|
|
43
43
|
"@codemirror/lang-sql": "6.10.0",
|
|
44
|
-
"@columenlabs/lumen-ui-types": "0.1.
|
|
44
|
+
"@columenlabs/lumen-ui-types": "0.1.65",
|
|
45
45
|
"@dnd-kit/core": "6.3.1",
|
|
46
46
|
"@dnd-kit/sortable": "10.0.0",
|
|
47
47
|
"@dnd-kit/utilities": "3.2.2",
|