@columenlabs/lumen-ui 0.1.237 → 0.1.239
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.
|
@@ -19,7 +19,7 @@ import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTi
|
|
|
19
19
|
import { InviteDrawer } from './invite-drawer.js';
|
|
20
20
|
import { PersonDrawer } from './person-drawer.js';
|
|
21
21
|
import { AuditSection, InvitesSection, PermissionsSection } from './sections.js';
|
|
22
|
-
import { LevelPicker, STATUS_LABEL, fromNow, peopleView, scopeKey, statusTone, topScopes, usePeopleAction, usePeopleQuery, useVisitView, when, withGrant, } from './shared.js';
|
|
22
|
+
import { LevelPicker, STATUS_LABEL, fromNow, peopleView, scopeKey, statusTone, syncText, topScopes, usePeopleAction, usePeopleQuery, useVisitView, when, withGrant, } from './shared.js';
|
|
23
23
|
export function LumenPeople({ adapter, crumbs = [], section: given, onSection, onSetUpMail, fields = [], }) {
|
|
24
24
|
const [own, setOwn] = useState('people');
|
|
25
25
|
const section = given ?? own;
|
|
@@ -167,8 +167,13 @@ function PeopleList({ frame, fields, onInvite, }) {
|
|
|
167
167
|
const invited = people.filter((p) => p.status === 'invited').length;
|
|
168
168
|
const active = people.filter((p) => p.status === 'active').length;
|
|
169
169
|
const disabled = people.filter((p) => p.status === 'disabled').length;
|
|
170
|
-
|
|
171
|
-
|
|
170
|
+
// The first-run note stands in for the table only when it has no row; a lone admin is a row.
|
|
171
|
+
const none = people.length === 0;
|
|
172
|
+
return (_jsxs(_Fragment, { children: [_jsx(LumenLayout, { crumbs: frame.crumbs, actions: frame.invite, intro: _jsxs("div", { className: "flex flex-col gap-3", children: [frame.tabs, _jsxs(TileRow, { fill: true, "aria-label": "People at a glance", children: [_jsx(Tile, { label: "People", value: people.length, caption: `${active} active${disabled ? ` · ${disabled} disabled` : ''}` }), _jsx(Tile, { label: "Admins", value: admins.length, caption: "Can\u2019t remove the last admin" }), _jsx(Tile, { label: "Invites pending", value: invited, caption: expired ? `${expired} expired` : 'none expired' }), view.sync ? (_jsx(Tile, { label: "Sign-in sync", value: syncText(view.sync), caption: !view.sync.configured
|
|
173
|
+
? 'Invites don’t reach sign-in'
|
|
174
|
+
: view.sync.at
|
|
175
|
+
? when(view.sync.at)
|
|
176
|
+
: view.sync.detail, valueClassName: !view.sync.configured || view.sync.ok ? undefined : 'text-loss' })) : (_jsx(Tile, { label: "Email", value: view.mail.configured ? 'Working' : 'Not set up', caption: view.mail.from ?? 'links only' }))] }), frame.mailNote, picked.size > 0 && (_jsx(BulkBar, { frame: frame, ids: [...picked], onClear: () => setPicked(new Set()) }))] }), domain: domain, ...layout, rows: people, load: load, state: none ? _jsx(FirstRun, { mail: view.mail.configured, onInvite: onInvite }) : undefined, selection: { keys: picked, onChange: setPicked }, rowTitle: (p) => p.name || p.email, onOpen: (p) => setOpenId(p.id) }), _jsx(LumenBoundary, { where: "people-person", what: "panel", children: _jsx(PersonDrawer, { adapter: adapter, view: view, person: open, onClose: () => setOpenId(null) }) })] }));
|
|
172
177
|
}
|
|
173
178
|
/** One change over the ticked people. Apply clears the selection; Undo puts the change back. */
|
|
174
179
|
function BulkBar({ frame, ids, onClear, }) {
|
|
@@ -14,7 +14,7 @@ import { LumenSelector } from '../lumen-selector.js';
|
|
|
14
14
|
import { NoteLine } from '../note-line.js';
|
|
15
15
|
import { Badge } from '../ui/badge.js';
|
|
16
16
|
import { Button } from '../ui/button.js';
|
|
17
|
-
import { LevelPicker, STATUS_LABEL, adminGuard, fromNow, topScopes, usePeopleAction, when, withGrant, } from './shared.js';
|
|
17
|
+
import { LevelPicker, STATUS_LABEL, adminGuard, fromNow, syncText, topScopes, usePeopleAction, when, withGrant, } from './shared.js';
|
|
18
18
|
const ROLES = [
|
|
19
19
|
{ value: 'member', label: 'Member', description: 'Only the levels set per scope' },
|
|
20
20
|
{ value: 'admin', label: 'Admin', description: 'Top level everywhere, and manages People' },
|
|
@@ -47,7 +47,9 @@ export function PersonDrawer({ adapter, view, person, onClose, }) {
|
|
|
47
47
|
},
|
|
48
48
|
view.sync && {
|
|
49
49
|
label: 'Sign-in provider',
|
|
50
|
-
value:
|
|
50
|
+
value: view.sync.at
|
|
51
|
+
? `${syncText(view.sync)} · ${when(view.sync.at)}`
|
|
52
|
+
: syncText(view.sync),
|
|
51
53
|
},
|
|
52
54
|
] }) }), adapter.audit && _jsx(ActivitySection, { adapter: adapter, person: p }), _jsx(DangerSection, { adapter: adapter, person: p, guard: guard, onGone: onClose })] })) }));
|
|
53
55
|
}
|
package/dist/people/shared.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Grant, type PeopleView, type Person, type PersonStatus, type Scope } from '@columenlabs/lumen-ui-types/people';
|
|
1
|
+
import { type Grant, type PeopleView, type Person, type PersonStatus, type Scope, type SyncStatus } from '@columenlabs/lumen-ui-types/people';
|
|
2
2
|
import { type CustomViewConfig } from '@columenlabs/lumen-ui-types/views';
|
|
3
3
|
import { type ReactNode } from 'react';
|
|
4
4
|
import type { Crumb } from '../page.js';
|
|
@@ -12,6 +12,8 @@ export declare const statusTone: (p: Pick<Person, 'status'>) => ViewChipTone | u
|
|
|
12
12
|
*/
|
|
13
13
|
export declare function when(ts: number | null | undefined, now?: number): string;
|
|
14
14
|
/** `in 5 d`, `in 3 h`, `2 d ago`: how far a time is from now. */
|
|
15
|
+
/** A sign-in sync's one-word state: never set up is not a failed sync. */
|
|
16
|
+
export declare const syncText: (s: Pick<SyncStatus, 'configured' | 'ok'>) => "Not set up" | "Out of step" | "Synced";
|
|
15
17
|
export declare function fromNow(ts: number | null | undefined, now?: number): string;
|
|
16
18
|
/** The top scopes (apps), in order; constructs inside one have a `parent`. */
|
|
17
19
|
export declare const topScopes: (scopes: readonly Scope[]) => Scope[];
|
package/dist/people/shared.js
CHANGED
|
@@ -53,6 +53,8 @@ export function when(ts, now = Date.now()) {
|
|
|
53
53
|
}));
|
|
54
54
|
}
|
|
55
55
|
/** `in 5 d`, `in 3 h`, `2 d ago`: how far a time is from now. */
|
|
56
|
+
/** A sign-in sync's one-word state: never set up is not a failed sync. */
|
|
57
|
+
export const syncText = (s) => !s.configured ? 'Not set up' : s.ok ? 'Synced' : 'Out of step';
|
|
56
58
|
export function fromNow(ts, now = Date.now()) {
|
|
57
59
|
if (!ts)
|
|
58
60
|
return '—';
|
package/dist/setting-parts.js
CHANGED
|
@@ -24,9 +24,12 @@ export function SettingField({ label, info, htmlFor, end, stack = false, classNa
|
|
|
24
24
|
const query = useContext(SettingsSearch);
|
|
25
25
|
if (query && !settingMatches(query, label, info))
|
|
26
26
|
return null;
|
|
27
|
-
return (_jsxs("div", { "data-slot": "setting-field", "data-stack": stack || undefined, className: cn('flex min-h-10 min-w-0 items-center gap-3 border-t border-border/60 py-1.5 first:border-t-0', stack && 'max-sm:flex-wrap max-sm:gap-y-1.5', className), children: [_jsxs("div", { className: cn(
|
|
27
|
+
return (_jsxs("div", { "data-slot": "setting-field", "data-stack": stack || undefined, className: cn('flex min-h-10 min-w-0 items-center gap-3 border-t border-border/60 py-1.5 first:border-t-0', stack && 'max-sm:flex-wrap max-sm:gap-y-1.5', className), children: [_jsxs("div", { className: cn(
|
|
28
|
+
// At least the usual column, wider for a longer name, so a label is never cut; on a phone
|
|
29
|
+
// it may take up to half the row and wraps there instead of cutting.
|
|
30
|
+
'flex min-w-32 shrink-0 items-center gap-1.5 max-sm:min-w-24 max-sm:max-w-[50%] max-sm:shrink', stack && 'max-sm:w-auto max-sm:flex-1',
|
|
28
31
|
// Nothing beside it but `end`: the label takes the row, so a short name never cuts off.
|
|
29
|
-
children === undefined && 'w-auto min-w-0 flex-1 shrink max-sm:w-auto'), children: [_jsx(Label, { htmlFor: htmlFor, className: "
|
|
32
|
+
children === undefined && 'w-auto min-w-0 flex-1 shrink max-sm:w-auto'), children: [_jsx(Label, { htmlFor: htmlFor, className: "whitespace-nowrap font-normal text-muted-foreground max-sm:whitespace-normal max-sm:break-words", title: label, children: label }), info && _jsx(InfoTip, { label: `About ${label}`, children: info })] }), children !== undefined && (_jsx("div", { className: cn('flex min-w-0 max-w-md flex-1 items-center gap-2', stack && 'max-sm:order-2 max-sm:max-w-none max-sm:basis-full'), children: children })), end && _jsx("div", { className: cn('ml-auto shrink-0', stack && 'max-sm:order-1'), children: end })] }));
|
|
30
33
|
}
|
|
31
34
|
/** A value read in place: cut to one line with the full text on hover, with Copy and Replace inside. */
|
|
32
35
|
export function SettingValue({ value, label, mono = false, copy = false, onReplace, replaceLabel = 'Replace', replaceIcon: ReplaceIcon = PencilIcon, }) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@columenlabs/lumen-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.239",
|
|
4
4
|
"description": "Lumen: shared React components (frame, views, widgets, charts, drawers).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"@codemirror/lang-javascript": "6.2.5",
|
|
50
50
|
"@codemirror/lang-json": "6.0.2",
|
|
51
51
|
"@codemirror/lang-sql": "6.10.0",
|
|
52
|
-
"@columenlabs/lumen-ui-types": "0.1.
|
|
52
|
+
"@columenlabs/lumen-ui-types": "0.1.239",
|
|
53
53
|
"@dnd-kit/core": "6.3.1",
|
|
54
54
|
"@dnd-kit/sortable": "10.0.0",
|
|
55
55
|
"@dnd-kit/utilities": "3.2.2",
|