@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: the whole fleet, one partner, one customer. Only when the viewer has more than one.
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;AAe/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;CAC9B,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;CAClD,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,kBAAkB,qBA6FhG"}
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: the whole fleet, one partner, one customer. Only when the viewer has more than one.
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 currentLabel = options.find((option) => option.value === value)?.label ?? value;
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) => (_jsx(CommandItem, { value: option.value, keywords: [
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] })] })] })] })) : (_jsx("span", { className: "truncate px-3 text-sm", children: currentLabel }))] }));
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: the whole fleet, one partner, one customer. Only when the viewer has more than one.",
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.117",
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.117",
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",