@columenlabs/lumen-ui 0.1.211 → 0.1.212

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.
@@ -130,7 +130,7 @@ function useDuplicateWarning(all) {
130
130
  }
131
131
  }, [all]);
132
132
  }
133
- const ROW = 'relative flex w-full cursor-default items-center gap-2 rounded-md px-2 text-sm outline-hidden select-none h-8 max-md:h-11 max-md:px-4 max-md:text-[15px] data-highlighted:bg-accent data-highlighted:text-accent-foreground data-selected:bg-muted data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4';
133
+ const ROW = 'relative flex w-full cursor-default items-center gap-2 rounded-md px-2 text-sm outline-hidden select-none h-8 max-md:h-11 max-md:px-4 max-md:text-[15px] data-highlighted:bg-accent data-highlighted:text-accent-foreground data-selected:bg-muted data-disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4';
134
134
  /** A row with a description or thumbnail grows to fit it. */
135
135
  const TALL = 'h-auto min-h-8 py-1.5 max-md:h-auto';
136
136
  const GROUP_LABEL = 'sticky top-0 z-10 bg-popover px-2 pt-2 pb-1 text-xs font-medium text-muted-foreground max-md:px-4';
@@ -138,12 +138,14 @@ const MORE = 'flex h-7 w-full items-center gap-1.5 rounded-md pr-2 pl-8 text-lef
138
138
  /** One row's inside: box or icon, label, detail, and the check when picked. */
139
139
  export function SelectorRowContent({ option, selected, multiple, trailing, }) {
140
140
  const Icon = option.icon;
141
- return (_jsxs(_Fragment, { children: [option.indent && _jsx("span", { "aria-hidden": true, className: "w-4 shrink-0" }), multiple && (_jsx("span", { "aria-hidden": true, className: cn('flex size-4 shrink-0 items-center justify-center rounded-[4px] border border-input', selected && 'border-primary bg-primary text-primary-foreground'), children: selected && _jsx(CheckIcon, { className: "size-3" }) })), option.thumb ? (_jsx("span", { "aria-hidden": true, className: "shrink-0", children: option.thumb })) : (Icon && _jsx(Icon, { className: "text-muted-foreground" })), option.description ? (_jsxs("span", { className: "flex min-w-0 flex-1 flex-col", children: [_jsx("span", { className: "truncate", children: option.content ?? option.label }), _jsx("span", { className: "truncate text-xs text-muted-foreground", children: option.description })] })) : (_jsx("span", { className: cn('min-w-0 truncate', option.detail ? 'max-w-2/3 shrink-0' : 'flex-1'), children: option.content ?? option.label })), option.detail && (_jsx("span", { className: "min-w-0 flex-1 truncate text-right text-xs text-muted-foreground", title: option.detail, children: option.detail })), trailing] }));
141
+ const off = !!option.disabled;
142
+ const reason = typeof option.disabled === 'string' ? option.disabled : null;
143
+ return (_jsxs(_Fragment, { children: [option.indent && _jsx("span", { "aria-hidden": true, className: "w-4 shrink-0" }), multiple && (_jsx("span", { "aria-hidden": true, className: cn('flex size-4 shrink-0 items-center justify-center rounded-[4px] border border-input', selected && 'border-primary bg-primary text-primary-foreground'), children: selected && _jsx(CheckIcon, { className: "size-3" }) })), option.thumb ? (_jsx("span", { "aria-hidden": true, className: "shrink-0", children: option.thumb })) : (Icon && _jsx(Icon, { className: cn('text-muted-foreground', off && 'opacity-60') })), option.description ? (_jsxs("span", { className: cn('flex min-w-0 flex-1 flex-col', off && 'text-muted-foreground'), children: [_jsx("span", { className: "truncate", children: option.content ?? option.label }), _jsx("span", { className: "truncate text-xs text-muted-foreground", children: option.description })] })) : (_jsx("span", { className: cn('min-w-0 truncate', option.detail || reason ? 'max-w-2/3 shrink-0' : 'flex-1', off && 'text-muted-foreground'), children: option.content ?? option.label })), reason ? (_jsxs("span", { className: "ml-auto inline-flex shrink-0 items-center gap-1 rounded-full border px-2 py-px text-xs text-foreground/80", children: [_jsx(CheckIcon, { className: "size-3! text-emerald-600 dark:text-emerald-400" }), reason] })) : (option.detail && (_jsx("span", { className: "min-w-0 flex-1 truncate text-right text-xs text-muted-foreground", title: option.detail, children: option.detail }))), trailing] }));
142
144
  }
143
145
  function Rows({ shown, picked, multiple, full, expand, }) {
144
146
  return shown.map((g) => (_jsxs(Base.Group, { items: g.rows.map((o) => o.value), children: [g.label && _jsx(Base.GroupLabel, { className: GROUP_LABEL, children: g.label }), g.rows.map((o) => {
145
147
  const row = (_jsx(Base.Item, { value: o.value, disabled: !!o.disabled || (full && !picked.has(o.value)), className: cn(ROW, (o.description || o.thumb) && TALL), children: _jsx(SelectorRowContent, { option: o, selected: picked.has(o.value), multiple: multiple && o.value !== CREATE }) }, o.value));
146
- return typeof o.disabled === 'string' ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("div", { children: row }) }), _jsx(TooltipContent, { children: o.disabled })] }, o.value)) : (row);
148
+ return row;
147
149
  }), g.more > 0 && (_jsxs("button", { type: "button", className: MORE, onMouseDown: (e) => e.preventDefault(), onClick: () => expand(g.label), children: [_jsx(ChevronDownIcon, { className: "size-3.5" }), g.more, " more"] }))] }, g.label || '\u0000')));
148
150
  }
149
151
  const FIELD = "flex w-fit min-w-0 items-center justify-between gap-1.5 rounded-lg border border-input bg-transparent pr-2 pl-2.5 text-sm whitespace-nowrap transition-colors outline-none select-none dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4";
@@ -283,12 +285,13 @@ export function LumenSelector(props) {
283
285
  export function LumenSelectorList({ options, groups, query, onPick, onClose, empty, header, className, handle, label, fuzzy, }) {
284
286
  const none = useMemo(() => new Set(), []);
285
287
  const { shown, flat, expand } = useSelectorRows(options, groups, query, none, fuzzy);
286
- const [at, setAt] = useState(0);
288
+ const first = flat.findIndex((o) => !o.disabled);
289
+ const [at, setAt] = useState(first);
287
290
  const listId = useId();
288
- // biome-ignore lint/correctness/useExhaustiveDependencies: a new query starts at the first row.
289
- useEffect(() => setAt(0), [query]);
290
- const index = Math.min(at, flat.length - 1);
291
- const active = flat[index];
291
+ // biome-ignore lint/correctness/useExhaustiveDependencies: a new query starts at the first pickable row.
292
+ useEffect(() => setAt(first), [query]);
293
+ const index = flat[at] && !flat[at].disabled ? at : first;
294
+ const active = index < 0 ? undefined : flat[index];
292
295
  useEffect(() => {
293
296
  spotlight(active?.target, active?.targetName ?? active?.label);
294
297
  }, [active]);
@@ -300,7 +303,13 @@ export function LumenSelectorList({ options, groups, query, onPick, onClose, emp
300
303
  if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
301
304
  e.preventDefault();
302
305
  const step = e.key === 'ArrowDown' ? 1 : -1;
303
- setAt((i) => (Math.min(i, flat.length - 1) + step + flat.length) % flat.length);
306
+ if (index < 0)
307
+ return true;
308
+ let i = index;
309
+ do
310
+ i = (i + step + flat.length) % flat.length;
311
+ while (flat[i]?.disabled && i !== index);
312
+ setAt(i);
304
313
  return true;
305
314
  }
306
315
  if ((e.key === 'Enter' && !e.shiftKey) || e.key === 'Tab') {
@@ -317,9 +326,9 @@ export function LumenSelectorList({ options, groups, query, onPick, onClose, emp
317
326
  }
318
327
  return false;
319
328
  },
320
- }), [flat, active, onPick, onClose]);
329
+ }), [flat, index, active, onPick, onClose]);
321
330
  return (_jsxs("div", { "data-slot": "lumen-selector-list", className: cn('flex max-h-80 flex-col overflow-hidden rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10', className), children: [header, _jsxs("div", { id: listId, "aria-label": label, className: "min-h-0 overflow-y-auto overscroll-contain p-1", children: [!flat.length && (_jsx("p", { className: "px-2 py-3 text-center text-sm text-muted-foreground", children: empty ?? 'Nothing matches' })), shown.map((g) => (_jsxs("section", { "aria-label": g.label || undefined, children: [g.label && _jsx("div", { className: GROUP_LABEL, children: g.label }), g.rows.map((o) => {
322
331
  const i = flat.indexOf(o);
323
- return (_jsx("button", { type: "button", tabIndex: -1, "aria-current": i === index || undefined, disabled: !!o.disabled, "data-highlighted": i === index ? '' : undefined, className: cn(ROW, 'text-left'), onMouseMove: () => i !== index && setAt(i), onMouseDown: (e) => e.preventDefault(), onClick: () => !o.disabled && onPick(o), children: _jsx(SelectorRowContent, { option: o, trailing: i === 0 ? (_jsx("kbd", { className: "rounded border px-1 font-mono text-[11px] text-muted-foreground", children: "Tab" })) : undefined }) }, o.value));
332
+ return (_jsx("button", { type: "button", tabIndex: -1, "aria-current": i === index || undefined, disabled: !!o.disabled, "data-disabled": o.disabled ? '' : undefined, "data-highlighted": i === index ? '' : undefined, className: cn(ROW, 'text-left'), onMouseMove: () => i !== index && setAt(i), onMouseDown: (e) => e.preventDefault(), onClick: () => !o.disabled && onPick(o), children: _jsx(SelectorRowContent, { option: o, trailing: i === index ? (_jsx("kbd", { className: "rounded border px-1 font-mono text-[11px] text-muted-foreground", children: "Tab" })) : undefined }) }, o.value));
324
333
  }), g.more > 0 && (_jsxs("button", { type: "button", className: MORE, onMouseDown: (e) => e.preventDefault(), onClick: () => expand(g.label), children: [_jsx(ChevronDownIcon, { className: "size-3.5" }), g.more, " more ", g.label.toLowerCase()] }))] }, g.label || '\u0000')))] })] }));
325
334
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@columenlabs/lumen-ui",
3
- "version": "0.1.211",
3
+ "version": "0.1.212",
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.211",
52
+ "@columenlabs/lumen-ui-types": "0.1.212",
53
53
  "@dnd-kit/core": "6.3.1",
54
54
  "@dnd-kit/sortable": "10.0.0",
55
55
  "@dnd-kit/utilities": "3.2.2",