@the-portland-company/shell 0.11.3 → 0.11.4

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.
@@ -0,0 +1,49 @@
1
+ import { FiUser, FiSettings, FiMessageSquare, FiMail, FiInfo, FiSun, FiMoon, FiHelpCircle, FiLifeBuoy, FiAlertCircle, FiAlertTriangle, FiBell, FiGrid, FiZap } from 'react-icons/fi';
2
+ import { useState, useEffect } from 'react';
3
+
4
+ // src/lib/brandGlyphs.tsx
5
+ var GLYPHS = {
6
+ bolt: FiZap,
7
+ zap: FiZap,
8
+ grid: FiGrid,
9
+ apps: FiGrid,
10
+ bell: FiBell,
11
+ notification: FiBell,
12
+ "alert-triangle": FiAlertTriangle,
13
+ alert: FiAlertTriangle,
14
+ "alert-circle": FiAlertCircle,
15
+ "life-buoy": FiLifeBuoy,
16
+ "life-ring": FiLifeBuoy,
17
+ support: FiLifeBuoy,
18
+ "help-circle": FiHelpCircle,
19
+ moon: FiMoon,
20
+ sun: FiSun,
21
+ info: FiInfo,
22
+ mail: FiMail,
23
+ "message-square": FiMessageSquare,
24
+ settings: FiSettings,
25
+ user: FiUser
26
+ };
27
+ function resolveBrandGlyph(name) {
28
+ if (!name) return null;
29
+ return GLYPHS[name.trim().toLowerCase()] ?? null;
30
+ }
31
+ function useTopBarIconSlotGlyph(slot) {
32
+ const [name, setName] = useState(null);
33
+ useEffect(() => {
34
+ if (typeof window === "undefined") return;
35
+ const read = () => {
36
+ const raw = window.getComputedStyle(document.documentElement).getPropertyValue(`--topbar-icon-${slot}`).trim().replace(/^['"]|['"]$/g, "");
37
+ setName(raw.length > 0 ? raw : null);
38
+ };
39
+ read();
40
+ const observer = new MutationObserver(read);
41
+ observer.observe(document.head, { childList: true, subtree: true });
42
+ return () => observer.disconnect();
43
+ }, [slot]);
44
+ return name;
45
+ }
46
+
47
+ export { resolveBrandGlyph, useTopBarIconSlotGlyph };
48
+ //# sourceMappingURL=chunk-2GFWARYP.js.map
49
+ //# sourceMappingURL=chunk-2GFWARYP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/brandGlyphs.tsx","../src/lib/topbarIconSlot.ts"],"names":[],"mappings":";;;;AAgCA,IAAM,MAAA,GAAyC;AAAA,EAC7C,IAAA,EAAM,KAAA;AAAA,EACN,GAAA,EAAK,KAAA;AAAA,EACL,IAAA,EAAM,MAAA;AAAA,EACN,IAAA,EAAM,MAAA;AAAA,EACN,IAAA,EAAM,MAAA;AAAA,EACN,YAAA,EAAc,MAAA;AAAA,EACd,gBAAA,EAAkB,eAAA;AAAA,EAClB,KAAA,EAAO,eAAA;AAAA,EACP,cAAA,EAAgB,aAAA;AAAA,EAChB,WAAA,EAAa,UAAA;AAAA,EACb,WAAA,EAAa,UAAA;AAAA,EACb,OAAA,EAAS,UAAA;AAAA,EACT,aAAA,EAAe,YAAA;AAAA,EACf,IAAA,EAAM,MAAA;AAAA,EACN,GAAA,EAAK,KAAA;AAAA,EACL,IAAA,EAAM,MAAA;AAAA,EACN,IAAA,EAAM,MAAA;AAAA,EACN,gBAAA,EAAkB,eAAA;AAAA,EAClB,QAAA,EAAU,UAAA;AAAA,EACV,IAAA,EAAM;AACR,CAAA;AAOO,SAAS,kBAAkB,IAAA,EAAwD;AACxF,EAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAClB,EAAA,OAAO,OAAO,IAAA,CAAK,IAAA,EAAK,CAAE,WAAA,EAAa,CAAA,IAAK,IAAA;AAC9C;AClCO,SAAS,uBAAuB,IAAA,EAAqC;AAC1E,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAI,SAAwB,IAAI,CAAA;AAEpD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAEnC,IAAA,MAAM,OAAO,MAAM;AACjB,MAAA,MAAM,GAAA,GAAM,MAAA,CACT,gBAAA,CAAiB,QAAA,CAAS,eAAe,CAAA,CACzC,gBAAA,CAAiB,CAAA,cAAA,EAAiB,IAAI,EAAE,CAAA,CACxC,IAAA,EAAK,CAEL,OAAA,CAAQ,gBAAgB,EAAE,CAAA;AAC7B,MAAA,OAAA,CAAQ,GAAA,CAAI,MAAA,GAAS,CAAA,GAAI,GAAA,GAAM,IAAI,CAAA;AAAA,IACrC,CAAA;AAEA,IAAA,IAAA,EAAK;AAEL,IAAA,MAAM,QAAA,GAAW,IAAI,gBAAA,CAAiB,IAAI,CAAA;AAC1C,IAAA,QAAA,CAAS,OAAA,CAAQ,SAAS,IAAA,EAAM,EAAE,WAAW,IAAA,EAAM,OAAA,EAAS,MAAM,CAAA;AAClE,IAAA,OAAO,MAAM,SAAS,UAAA,EAAW;AAAA,EACnC,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AAET,EAAA,OAAO,IAAA;AACT","file":"chunk-2GFWARYP.js","sourcesContent":["import type { ComponentType } from 'react'\nimport {\n FiAlertCircle,\n FiAlertTriangle,\n FiBell,\n FiGrid,\n FiHelpCircle,\n FiInfo,\n FiLifeBuoy,\n FiMail,\n FiMessageSquare,\n FiMoon,\n FiSettings,\n FiSun,\n FiUser,\n FiZap,\n} from 'react-icons/fi'\n\nexport type GlyphComponent = ComponentType<{ size?: number | string; className?: string }>\n\n/**\n * Brand-glyph registry: maps the icon NAME the Brand API emits (e.g.\n * `--topbar-icon-comms-fuel: bolt`) to a component.\n *\n * These names are Feather/Lucide icon names, NOT asset filenames — there is no\n * `/brand/icons/<name>.svg` for them in any app, and treating them as URLs\n * renders a broken image. Resolving them against the icon library the shell\n * already depends on keeps every app consistent with zero vendored assets.\n *\n * Aliases are included where Brand and Feather disagree on a name (`bolt` vs\n * `zap`, `life-buoy` vs `life-ring`).\n */\nconst GLYPHS: Record<string, GlyphComponent> = {\n bolt: FiZap,\n zap: FiZap,\n grid: FiGrid,\n apps: FiGrid,\n bell: FiBell,\n notification: FiBell,\n 'alert-triangle': FiAlertTriangle,\n alert: FiAlertTriangle,\n 'alert-circle': FiAlertCircle,\n 'life-buoy': FiLifeBuoy,\n 'life-ring': FiLifeBuoy,\n support: FiLifeBuoy,\n 'help-circle': FiHelpCircle,\n moon: FiMoon,\n sun: FiSun,\n info: FiInfo,\n mail: FiMail,\n 'message-square': FiMessageSquare,\n settings: FiSettings,\n user: FiUser,\n}\n\n/**\n * Resolve a Brand glyph name to a component. Returns null for an unknown name\n * so the caller renders its own fallback — an unrecognized token must never\n * produce a broken or empty icon.\n */\nexport function resolveBrandGlyph(name: string | null | undefined): GlyphComponent | null {\n if (!name) return null\n return GLYPHS[name.trim().toLowerCase()] ?? null\n}\n\n/** Names the registry knows — exported for tests and tooling. */\nexport const KNOWN_BRAND_GLYPHS = Object.keys(GLYPHS)\n","import { useEffect, useState } from 'react'\n\n/**\n * The six canonical top-bar action slots (Reference/Global-Rules v9 → top-bar\n * icons). Each slot's GLYPH is unique and comes from the Brand API\n * `topbarIcons.items.*` tokens, exposed as `--topbar-icon-<slot>` CSS vars\n * whose value is the brand-icon NAME. Every OTHER property — size, gap, color,\n * hover, selected — is universal across all six slots.\n */\nexport type TopBarIconSlot =\n | 'comms-fuel'\n | 'apps'\n | 'notifications'\n | 'alerts'\n | 'support'\n | 'dark-mode'\n\n/**\n * Reads the glyph NAME for a slot from its `--topbar-icon-<slot>` CSS var.\n *\n * Runs in an effect because the Brand stylesheet is appended to <head> at\n * runtime (Global Rules forbids baking it into the build), so the var is not\n * resolvable during SSR or the first synchronous render. The MutationObserver\n * re-reads once the stylesheet lands so the glyph upgrades from the caller's\n * fallback without a reload.\n *\n * Returns null when the slot has no Brand glyph — callers render their own\n * fallback icon in that case.\n */\nexport function useTopBarIconSlotGlyph(slot: TopBarIconSlot): string | null {\n const [name, setName] = useState<string | null>(null)\n\n useEffect(() => {\n if (typeof window === 'undefined') return\n\n const read = () => {\n const raw = window\n .getComputedStyle(document.documentElement)\n .getPropertyValue(`--topbar-icon-${slot}`)\n .trim()\n // Brand may emit the name quoted (e.g. \"bell\"); strip the quotes.\n .replace(/^['\"]|['\"]$/g, '')\n setName(raw.length > 0 ? raw : null)\n }\n\n read()\n\n const observer = new MutationObserver(read)\n observer.observe(document.head, { childList: true, subtree: true })\n return () => observer.disconnect()\n }, [slot])\n\n return name\n}\n\n/** Path to a brand-icon SVG in the consuming app's public dir. */\nexport function brandIconSrc(name: string, basePath = '/brand/icons/') {\n return `${basePath}${name}.svg`\n}\n"]}
package/dist/index.cjs CHANGED
@@ -2655,6 +2655,32 @@ function AppLayout({
2655
2655
  overlaySlot
2656
2656
  ] });
2657
2657
  }
2658
+ var GLYPHS = {
2659
+ bolt: fi.FiZap,
2660
+ zap: fi.FiZap,
2661
+ grid: fi.FiGrid,
2662
+ apps: fi.FiGrid,
2663
+ bell: fi.FiBell,
2664
+ notification: fi.FiBell,
2665
+ "alert-triangle": fi.FiAlertTriangle,
2666
+ alert: fi.FiAlertTriangle,
2667
+ "alert-circle": fi.FiAlertCircle,
2668
+ "life-buoy": fi.FiLifeBuoy,
2669
+ "life-ring": fi.FiLifeBuoy,
2670
+ support: fi.FiLifeBuoy,
2671
+ "help-circle": fi.FiHelpCircle,
2672
+ moon: fi.FiMoon,
2673
+ sun: fi.FiSun,
2674
+ info: fi.FiInfo,
2675
+ mail: fi.FiMail,
2676
+ "message-square": fi.FiMessageSquare,
2677
+ settings: fi.FiSettings,
2678
+ user: fi.FiUser
2679
+ };
2680
+ function resolveBrandGlyph(name) {
2681
+ if (!name) return null;
2682
+ return GLYPHS[name.trim().toLowerCase()] ?? null;
2683
+ }
2658
2684
  function useTopBarIconSlotGlyph(slot) {
2659
2685
  const [name, setName] = react$1.useState(null);
2660
2686
  react$1.useEffect(() => {
@@ -2673,16 +2699,8 @@ function useTopBarIconSlotGlyph(slot) {
2673
2699
  var TopBarIcon = react$1.forwardRef(
2674
2700
  function TopBarIcon2({ slot, fallbackIcon, isSelected, ...rest }, ref) {
2675
2701
  const glyphName = useTopBarIconSlotGlyph(slot);
2676
- const icon = glyphName ? /* @__PURE__ */ jsxRuntime.jsx(
2677
- BrandIcon,
2678
- {
2679
- name: glyphName,
2680
- style: {
2681
- width: "var(--topbar-icon-size, 20px)",
2682
- height: "var(--topbar-icon-size, 20px)"
2683
- }
2684
- }
2685
- ) : fallbackIcon;
2702
+ const Glyph = resolveBrandGlyph(glyphName);
2703
+ const icon = Glyph ? /* @__PURE__ */ jsxRuntime.jsx(Glyph, { size: "var(--topbar-icon-size, 20px)" }) : fallbackIcon;
2686
2704
  return /* @__PURE__ */ jsxRuntime.jsx(
2687
2705
  react.IconButton,
2688
2706
  {