@nadicodeai/ui 12.1.117 → 12.1.118
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.
|
@@ -4,6 +4,7 @@ export type ScopeOption = {
|
|
|
4
4
|
label: string;
|
|
5
5
|
group?: string;
|
|
6
6
|
keywords?: readonly string[];
|
|
7
|
+
mark?: React.ReactNode;
|
|
7
8
|
};
|
|
8
9
|
export type ScopeSwitcherCopy = {
|
|
9
10
|
label: string;
|
|
@@ -22,16 +23,17 @@ export type ScopeSwitcherProps = {
|
|
|
22
23
|
label: string;
|
|
23
24
|
onSelect: () => void;
|
|
24
25
|
};
|
|
26
|
+
variant?: "scope" | "navigation";
|
|
25
27
|
};
|
|
26
28
|
/**
|
|
27
29
|
* Says which part of the account a page reads, and changes it.
|
|
28
30
|
*
|
|
29
31
|
* @family Navigation
|
|
30
|
-
* @when Changing which part of the account a page reads, such as Analytics' scope
|
|
32
|
+
* @when Changing which part of the account a page reads, such as Analytics' scope, or switching between objects from the shell's top bar with `variant="navigation"`.
|
|
31
33
|
* @instead A viewer with one scope and nothing to create reads its name with no control, which this draws by itself. Finding anything is `CommandDialog`; narrowing rows is `CollectionFilters`.
|
|
32
|
-
* @behaviour A ghost trigger, at most 16rem, opens a 288 px popover with focus in its search. Type to filter, Enter chooses and closes; choosing the current one changes nothing. The caller writes the choice to the URL in `onValueChange`, so sibling pages keep it. An optional create command sits last.
|
|
34
|
+
* @behaviour A ghost trigger, at most 16rem, opens a 288 px popover with focus in its search. Navigation omits the visible prefix, carries each option's identity mark and opens a 384 px popover constrained to the viewport. Type to filter, Enter chooses and closes; choosing the current one changes nothing. The caller writes the choice to the URL in `onValueChange`, so sibling pages keep it. An optional create command sits last.
|
|
33
35
|
* @words `copy.label` names what is chosen ("Scope") beside the current scope's name. Search and empty lines name the thing: "Find a scope", "No scope matches".
|
|
34
36
|
* @access The trigger reads "Scope: All customers"; the popover has a hidden title; options are a listbox with the current one checked.
|
|
35
37
|
*/
|
|
36
|
-
export declare function ScopeSwitcher({ value, options, onValueChange, copy, create }: ScopeSwitcherProps): React.JSX.Element;
|
|
38
|
+
export declare function ScopeSwitcher({ value, options, onValueChange, copy, create, variant, }: ScopeSwitcherProps): React.JSX.Element;
|
|
37
39
|
//# sourceMappingURL=scope-switcher.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scope-switcher.d.ts","sourceRoot":"","sources":["../../src/components/scope-switcher.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"scope-switcher.d.ts","sourceRoot":"","sources":["../../src/components/scope-switcher.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAgB/B,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,+FAA+F;IAC/F,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IAChC,8DAA8D;IAC9D,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,IAAI,EAAE,iBAAiB,CAAC;IACxB,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC;IACjD,OAAO,CAAC,EAAE,OAAO,GAAG,YAAY,CAAC;CAClC,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,OAAO,EACP,aAAa,EACb,IAAI,EACJ,MAAM,EACN,OAAiB,GAClB,EAAE,kBAAkB,qBA0GpB"}
|
|
@@ -5,20 +5,22 @@ import { ChevronsUpDown, Plus } from "lucide-react";
|
|
|
5
5
|
import { Button } from "./button.js";
|
|
6
6
|
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, } from "./command.js";
|
|
7
7
|
import { Popover, PopoverContent, PopoverTitle, PopoverTrigger } from "./popover.js";
|
|
8
|
+
import { cn } from "../lib/utils.js";
|
|
8
9
|
/**
|
|
9
10
|
* Says which part of the account a page reads, and changes it.
|
|
10
11
|
*
|
|
11
12
|
* @family Navigation
|
|
12
|
-
* @when Changing which part of the account a page reads, such as Analytics' scope
|
|
13
|
+
* @when Changing which part of the account a page reads, such as Analytics' scope, or switching between objects from the shell's top bar with `variant="navigation"`.
|
|
13
14
|
* @instead A viewer with one scope and nothing to create reads its name with no control, which this draws by itself. Finding anything is `CommandDialog`; narrowing rows is `CollectionFilters`.
|
|
14
|
-
* @behaviour A ghost trigger, at most 16rem, opens a 288 px popover with focus in its search. Type to filter, Enter chooses and closes; choosing the current one changes nothing. The caller writes the choice to the URL in `onValueChange`, so sibling pages keep it. An optional create command sits last.
|
|
15
|
+
* @behaviour A ghost trigger, at most 16rem, opens a 288 px popover with focus in its search. Navigation omits the visible prefix, carries each option's identity mark and opens a 384 px popover constrained to the viewport. Type to filter, Enter chooses and closes; choosing the current one changes nothing. The caller writes the choice to the URL in `onValueChange`, so sibling pages keep it. An optional create command sits last.
|
|
15
16
|
* @words `copy.label` names what is chosen ("Scope") beside the current scope's name. Search and empty lines name the thing: "Find a scope", "No scope matches".
|
|
16
17
|
* @access The trigger reads "Scope: All customers"; the popover has a hidden title; options are a listbox with the current one checked.
|
|
17
18
|
*/
|
|
18
|
-
export function ScopeSwitcher({ value, options, onValueChange, copy, create }) {
|
|
19
|
+
export function ScopeSwitcher({ value, options, onValueChange, copy, create, variant = "scope", }) {
|
|
19
20
|
const [open, setOpen] = React.useState(false);
|
|
20
21
|
const search = React.useRef(null);
|
|
21
|
-
const
|
|
22
|
+
const current = options.find((option) => option.value === value);
|
|
23
|
+
const currentLabel = current?.label ?? value;
|
|
22
24
|
const groups = React.useMemo(() => {
|
|
23
25
|
const byGroup = new Map();
|
|
24
26
|
for (const option of options) {
|
|
@@ -34,12 +36,12 @@ export function ScopeSwitcher({ value, options, onValueChange, copy, create }) {
|
|
|
34
36
|
onValueChange(next);
|
|
35
37
|
};
|
|
36
38
|
const switchable = options.length > 1 || create !== undefined;
|
|
37
|
-
return (_jsxs("span", { "data-slot": "scope-switcher", className: "flex min-w-0 items-center gap-2", children: [_jsx("span", { "aria-hidden": switchable ? "true" : undefined, className: "text-xs font-medium text-muted-foreground", children: copy.label }), switchable ? (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsxs(PopoverTrigger, { render: _jsx(Button, { variant: "ghost", className: "max-w-64", suffix: _jsx(ChevronsUpDown, { "aria-hidden": "true" }) }), children: [_jsxs("span", { className: "sr-only", children: [copy.label, ":"] }), " ", _jsx("span", { className: "truncate", children: currentLabel })] }), _jsxs(PopoverContent, { align: "start", className: "w-72 p-0", initialFocus: search, children: [_jsx(PopoverTitle, { className: "sr-only", children: copy.label }), _jsxs(Command, { label: copy.search, resultCount: copy.results, children: [_jsx(CommandInput, { ref: search, "aria-label": copy.search, placeholder: copy.search }), _jsxs(CommandList, { label: copy.label, children: [_jsx(CommandEmpty, { children: copy.empty }), groups.map(([group, items]) => (_jsx(CommandGroup, { heading: group, children: items.map((option) => (
|
|
39
|
+
return (_jsxs("span", { "data-slot": "scope-switcher", className: "flex min-w-0 items-center gap-2", children: [_jsx("span", { "aria-hidden": switchable ? "true" : undefined, className: cn("text-xs font-medium text-muted-foreground", variant === "navigation" && "sr-only"), children: copy.label }), switchable ? (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsxs(PopoverTrigger, { render: _jsx(Button, { variant: "ghost", className: "min-w-0 max-w-64 shrink", prefix: current?.mark ? _jsx("span", { "aria-hidden": "true", children: current.mark }) : undefined, suffix: _jsx(ChevronsUpDown, { "aria-hidden": "true" }) }), children: [_jsxs("span", { className: "sr-only", children: [copy.label, ":"] }), " ", _jsx("span", { className: "truncate", children: currentLabel })] }), _jsxs(PopoverContent, { align: "start", className: cn("w-72 max-w-[calc(100vw-2rem)] p-0", variant === "navigation" && "w-96"), initialFocus: search, children: [_jsx(PopoverTitle, { className: "sr-only", children: copy.label }), _jsxs(Command, { label: copy.search, resultCount: copy.results, children: [_jsx(CommandInput, { ref: search, "aria-label": copy.search, placeholder: copy.search }), _jsxs(CommandList, { label: copy.label, children: [_jsx(CommandEmpty, { children: copy.empty }), groups.map(([group, items]) => (_jsx(CommandGroup, { heading: group, children: items.map((option) => (_jsxs(CommandItem, { value: option.value, keywords: [
|
|
38
40
|
option.label,
|
|
39
41
|
...(option.group ? [option.group] : []),
|
|
40
42
|
...(option.keywords ?? []),
|
|
41
|
-
], "data-checked": option.value === value, onSelect: () => choose(option.value), children: _jsx("span", { className: "truncate", children: option.label }) }, option.value))) }, group ?? ""))), create ? (_jsxs(_Fragment, { children: [_jsx(CommandSeparator, { alwaysRender: true }), _jsx(CommandGroup, { forceMount: true, heading: undefined, children: _jsxs(CommandItem, { value: `create ${create.label}`, forceMount: true, onSelect: () => {
|
|
43
|
+
], "data-checked": option.value === value, onSelect: () => choose(option.value), children: [option.mark ? _jsx("span", { "aria-hidden": "true", children: option.mark }) : null, _jsx("span", { className: "truncate", children: option.label })] }, option.value))) }, group ?? ""))), create ? (_jsxs(_Fragment, { children: [_jsx(CommandSeparator, { alwaysRender: true }), _jsx(CommandGroup, { forceMount: true, heading: undefined, children: _jsxs(CommandItem, { value: `create ${create.label}`, forceMount: true, onSelect: () => {
|
|
42
44
|
setOpen(false);
|
|
43
45
|
create.onSelect();
|
|
44
|
-
}, children: [_jsx(Plus, { "aria-hidden": "true" }), create.label] }) })] })) : null] })] })] })] })) : (
|
|
46
|
+
}, children: [_jsx(Plus, { "aria-hidden": "true" }), create.label] }) })] })) : null] })] })] })] })) : (_jsxs("span", { className: "flex min-w-0 items-center gap-2 px-3 text-sm", children: [current?.mark ? _jsx("span", { "aria-hidden": "true", children: current.mark }) : null, _jsx("span", { className: "truncate", children: currentLabel })] }))] }));
|
|
45
47
|
}
|
|
@@ -866,9 +866,9 @@ export const USAGE = [
|
|
|
866
866
|
module: "scope-switcher",
|
|
867
867
|
family: "Navigation",
|
|
868
868
|
summary: "Says which part of the account a page reads, and changes it.",
|
|
869
|
-
when: "Changing which part of the account a page reads, such as Analytics' scope
|
|
869
|
+
when: "Changing which part of the account a page reads, such as Analytics' scope, or switching between objects from the shell's top bar with `variant=\"navigation\"`.",
|
|
870
870
|
instead: "A viewer with one scope and nothing to create reads its name with no control, which this draws by itself. Finding anything is `CommandDialog`; narrowing rows is `CollectionFilters`.",
|
|
871
|
-
behaviour: "A ghost trigger, at most 16rem, opens a 288 px popover with focus in its search. Type to filter, Enter chooses and closes; choosing the current one changes nothing. The caller writes the choice to the URL in `onValueChange`, so sibling pages keep it. An optional create command sits last.",
|
|
871
|
+
behaviour: "A ghost trigger, at most 16rem, opens a 288 px popover with focus in its search. Navigation omits the visible prefix, carries each option's identity mark and opens a 384 px popover constrained to the viewport. Type to filter, Enter chooses and closes; choosing the current one changes nothing. The caller writes the choice to the URL in `onValueChange`, so sibling pages keep it. An optional create command sits last.",
|
|
872
872
|
words: '`copy.label` names what is chosen ("Scope") beside the current scope\'s name. Search and empty lines name the thing: "Find a scope", "No scope matches".',
|
|
873
873
|
access: 'The trigger reads "Scope: All customers"; the popover has a hidden title; options are a listbox with the current one checked.',
|
|
874
874
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nadicodeai/ui",
|
|
3
|
-
"version": "12.1.
|
|
3
|
+
"version": "12.1.118",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"@base-ui/react": "^1.9.0",
|
|
83
83
|
"@iconify-icons/logos": "^2.0.3",
|
|
84
84
|
"@iconify/react": "^6.0.2",
|
|
85
|
-
"@nadicodeai/design-system": "12.1.
|
|
85
|
+
"@nadicodeai/design-system": "12.1.118",
|
|
86
86
|
"@tanstack/react-table": "^9.3.1",
|
|
87
87
|
"@vgpu/wgsl": "0.6.0",
|
|
88
88
|
"class-variance-authority": "^0.7.1",
|