@columenlabs/lumen-ui 0.1.191 → 0.1.193

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.
@@ -18,7 +18,9 @@ import { InputGroup, InputGroupAddon, InputGroupButton } from './ui/input-group.
18
18
  import { Label } from './ui/label.js';
19
19
  /** One setting inside an item: its label, then its control group, then an optional switch at the end. */
20
20
  export function SettingField({ label, info, htmlFor, end, stack = false, className, children, }) {
21
- 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('flex w-32 shrink-0 items-center gap-1.5 max-sm:w-24', stack && 'max-sm:w-auto max-sm:flex-1'), children: [_jsx(Label, { htmlFor: htmlFor, className: "truncate font-normal text-muted-foreground", 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 })] }));
21
+ 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('flex w-32 shrink-0 items-center gap-1.5 max-sm:w-24', stack && 'max-sm:w-auto max-sm:flex-1',
22
+ // Nothing beside it but `end`: the label takes the row, so a short name never cuts off.
23
+ children === undefined && 'w-auto min-w-0 flex-1 shrink max-sm:w-auto'), children: [_jsx(Label, { htmlFor: htmlFor, className: "truncate font-normal text-muted-foreground", 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 })] }));
22
24
  }
23
25
  /** A value read in place: cut to one line with the full text on hover, with Copy and Replace inside. */
24
26
  export function SettingValue({ value, label, mono = false, copy = false, onReplace, replaceLabel = 'Replace', replaceIcon: ReplaceIcon = PencilIcon, }) {
@@ -11,7 +11,7 @@ export declare const viewIcon: (config: {
11
11
  export declare const iconOf: (icon: ViewIcon | null | undefined, fallback: NavIcon) => NavIcon;
12
12
  /** Every lucide icon name, for the icon picker's search: empty until `load` asks for the map and it lands. */
13
13
  export declare function useLucideIconNames(load?: boolean): readonly string[];
14
- /** Any lucide icon by name, loaded on first use (lucide's own DynamicIcon). */
14
+ /** Any lucide icon by name, loaded on first use (all icons in one lazy file). */
15
15
  export declare const lucideIcon: (name: string, fallback?: NavIcon) => NavIcon;
16
16
  /** An emoji drawn where an icon goes. */
17
17
  export declare const emojiIcon: (emoji: string) => NavIcon;
@@ -1,4 +1,4 @@
1
- import { BanknoteIcon, BriefcaseIcon, CalendarIcon, ChartBarIcon, ChartLineIcon, ChartPieIcon, ClockIcon, CoinsIcon, FlameIcon, Grid3x3Icon, LandmarkIcon, LayersIcon, ListTreeIcon, PiggyBankIcon, ReceiptIcon, ShieldIcon, StarIcon, TableIcon, TagIcon, TargetIcon, TrendingDownIcon, TrendingUpIcon, WalletIcon, ZapIcon, } from 'lucide-react';
1
+ import { BanknoteIcon, BriefcaseIcon, CalendarIcon, ChartBarIcon, ChartLineIcon, ChartPieIcon, ClockIcon, CoinsIcon, FlameIcon, Grid3x3Icon, Icon, LandmarkIcon, LayersIcon, ListTreeIcon, PiggyBankIcon, ReceiptIcon, ShieldIcon, StarIcon, TableIcon, TagIcon, TargetIcon, TrendingDownIcon, TrendingUpIcon, WalletIcon, ZapIcon, } from 'lucide-react';
2
2
  import { createElement, useEffect, useState } from 'react';
3
3
  import { cn } from './lib/utils.js';
4
4
  export const VIEW_ICONS = {
@@ -41,15 +41,32 @@ export const iconOf = (icon, fallback) => {
41
41
  if (icon in VIEW_ICONS)
42
42
  return VIEW_ICONS[icon];
43
43
  if (/^[a-z]/.test(icon))
44
- return lucide && !lucide.names.has(icon) ? fallback : lucideIcon(icon, fallback);
44
+ return lucide && !lucide.nodes.has(iconKey(icon)) ? fallback : lucideIcon(icon, fallback);
45
45
  return emojiIcon(icon);
46
46
  };
47
47
  let lucide;
48
48
  let loadingLucide;
49
+ /** A name's lookup key: `arrow-down-0-1`, `arrow-down-01` and `ArrowDown01` are one icon. */
50
+ const iconKey = (name) => name.replace(/-/g, '').toLowerCase();
51
+ /** `ArrowDown01` → `arrow-down-01`, the name the picker shows and saves. */
52
+ const kebab = (pascal) => pascal
53
+ .replace(/([a-z])([A-Z0-9])/g, '$1-$2')
54
+ .replace(/([0-9])([A-Za-z])/g, '$1-$2')
55
+ .replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')
56
+ .toLowerCase();
49
57
  function loadLucide() {
50
58
  if (!loadingLucide)
51
- loadingLucide = import('lucide-react/dynamic').then((m) => {
52
- lucide = { ...m, names: new Set(m.iconNames) };
59
+ loadingLucide = import('lucide').then(({ icons }) => {
60
+ const nodes = new Map();
61
+ const iconNames = [];
62
+ for (const [pascal, node] of Object.entries(icons)) {
63
+ const key = iconKey(pascal);
64
+ if (nodes.has(key))
65
+ continue;
66
+ nodes.set(key, node);
67
+ iconNames.push(kebab(pascal));
68
+ }
69
+ lucide = { iconNames: iconNames.sort(), nodes };
53
70
  return lucide;
54
71
  });
55
72
  return loadingLucide;
@@ -75,9 +92,10 @@ function LucideByName({ name, fallback, className, }) {
75
92
  'aria-hidden': true,
76
93
  className: cn('inline-block size-4 shrink-0', className),
77
94
  });
78
- if (!lucide.names.has(name))
95
+ const node = lucide.nodes.get(iconKey(name));
96
+ if (!node)
79
97
  return fallback ? createElement(fallback, { className }) : null;
80
- return createElement(lucide.DynamicIcon, { name: name, className });
98
+ return createElement(Icon, { iconNode: node, className });
81
99
  }
82
100
  const made = new Map();
83
101
  /** One component per pick, so React keeps it between renders. */
@@ -89,7 +107,7 @@ const once = (key, make) => {
89
107
  }
90
108
  return Icon;
91
109
  };
92
- /** Any lucide icon by name, loaded on first use (lucide's own DynamicIcon). */
110
+ /** Any lucide icon by name, loaded on first use (all icons in one lazy file). */
93
111
  export const lucideIcon = (name, fallback) => once(`lucide:${name}`, () => ({ className }) => createElement(LucideByName, { name, fallback, className }));
94
112
  /** An emoji drawn where an icon goes. */
95
113
  export const emojiIcon = (emoji) => once(`emoji:${emoji}`, () => ({ className }) => createElement('span', {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@columenlabs/lumen-ui",
3
- "version": "0.1.191",
3
+ "version": "0.1.193",
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.191",
52
+ "@columenlabs/lumen-ui-types": "0.1.193",
53
53
  "@dnd-kit/core": "6.3.1",
54
54
  "@dnd-kit/sortable": "10.0.0",
55
55
  "@dnd-kit/utilities": "3.2.2",
@@ -69,6 +69,7 @@
69
69
  "i18n-iso-countries": "7.14.0",
70
70
  "indicatorts": "^2.2.2",
71
71
  "lightweight-charts": "^5.2.1",
72
+ "lucide": "^1.48.0",
72
73
  "lucide-react": "^1.48.0",
73
74
  "papaparse": "5.5.3",
74
75
  "qrcode.react": "4.2.0",