@artstorefronts/arthelper-nav 0.1.0-alpha.0

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.
Files changed (51) hide show
  1. package/README.md +266 -0
  2. package/dist/config.d.ts +23 -0
  3. package/dist/config.d.ts.map +1 -0
  4. package/dist/config.js +134 -0
  5. package/dist/config.js.map +1 -0
  6. package/dist/context.d.ts +21 -0
  7. package/dist/context.d.ts.map +1 -0
  8. package/dist/context.js +12 -0
  9. package/dist/context.js.map +1 -0
  10. package/dist/cookie.d.ts +13 -0
  11. package/dist/cookie.d.ts.map +1 -0
  12. package/dist/cookie.js +28 -0
  13. package/dist/cookie.js.map +1 -0
  14. package/dist/fallback.d.ts +13 -0
  15. package/dist/fallback.d.ts.map +1 -0
  16. package/dist/fallback.js +17 -0
  17. package/dist/fallback.js.map +1 -0
  18. package/dist/icons.d.ts +14 -0
  19. package/dist/icons.d.ts.map +1 -0
  20. package/dist/icons.js +29 -0
  21. package/dist/icons.js.map +1 -0
  22. package/dist/index.css +509 -0
  23. package/dist/index.d.ts +13 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +8 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/nav-bar.d.ts +13 -0
  28. package/dist/nav-bar.d.ts.map +1 -0
  29. package/dist/nav-bar.js +91 -0
  30. package/dist/nav-bar.js.map +1 -0
  31. package/dist/nav-trigger.d.ts +16 -0
  32. package/dist/nav-trigger.d.ts.map +1 -0
  33. package/dist/nav-trigger.js +21 -0
  34. package/dist/nav-trigger.js.map +1 -0
  35. package/dist/provider.d.ts +19 -0
  36. package/dist/provider.d.ts.map +1 -0
  37. package/dist/provider.js +46 -0
  38. package/dist/provider.js.map +1 -0
  39. package/dist/resolve.d.ts +32 -0
  40. package/dist/resolve.d.ts.map +1 -0
  41. package/dist/resolve.js +51 -0
  42. package/dist/resolve.js.map +1 -0
  43. package/dist/theme.d.ts +13 -0
  44. package/dist/theme.d.ts.map +1 -0
  45. package/dist/theme.js +21 -0
  46. package/dist/theme.js.map +1 -0
  47. package/dist/types.d.ts +50 -0
  48. package/dist/types.d.ts.map +1 -0
  49. package/dist/types.js +10 -0
  50. package/dist/types.js.map +1 -0
  51. package/package.json +38 -0
@@ -0,0 +1,21 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useNav } from './context.js';
4
+ import { NavIcon, WaffleIcon } from './icons.js';
5
+ import { themeVars } from './theme.js';
6
+ /**
7
+ * The waffle plus active-app chip a host places by hand in its own header, beside
8
+ * its logo. This is the RESTORE control: while the bar is collapsed it is the only
9
+ * way back, so it is visibly interactive and always carries the restore label.
10
+ *
11
+ * It is display:none while the bar is expanded (and therefore out of the
12
+ * accessibility tree), which is why the label and aria-expanded are fixed rather
13
+ * than swapped — the bar's own waffle is the hide control.
14
+ */
15
+ function NavTrigger({ className }) {
16
+ const { config, activeApp, collapsed, toggle } = useNav();
17
+ const classes = ['ahsuite-docked', className ?? ''].filter(Boolean).join(' ');
18
+ return (_jsxs("button", { "aria-expanded": false, "aria-label": "Show the suite bar", className: classes, "data-collapsed": collapsed ? 'true' : 'false', "data-theme": config.theme.mode, onClick: toggle, style: themeVars(config.theme), title: "Show the suite bar", type: "button", children: [_jsx("span", { className: "ahsuite-chip ahsuite-chip--active", children: _jsx(NavIcon, { name: activeApp?.app.icon ?? '' }) }), _jsx(WaffleIcon, { className: "ahsuite-waffle-mini" })] }));
19
+ }
20
+ export { NavTrigger };
21
+ //# sourceMappingURL=nav-trigger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nav-trigger.js","sourceRoot":"","sources":["../src/nav-trigger.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAMvC;;;;;;;;GAQG;AACH,SAAS,UAAU,CAAC,EAAE,SAAS,EAAmB;IAChD,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;IAE1D,MAAM,OAAO,GAAG,CAAC,gBAAgB,EAAE,SAAS,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAE9E,OAAO,CACL,mCACiB,KAAK,gBACT,oBAAoB,EAC/B,SAAS,EAAE,OAAO,oBACF,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,gBAChC,MAAM,CAAC,KAAK,CAAC,IAAI,EAC7B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,EAC9B,KAAK,EAAC,oBAAoB,EAC1B,IAAI,EAAC,QAAQ,aAEb,eAAM,SAAS,EAAC,mCAAmC,YACjD,KAAC,OAAO,IAAC,IAAI,EAAE,SAAS,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE,GAAI,GACvC,EACP,KAAC,UAAU,IAAC,SAAS,EAAC,qBAAqB,GAAG,IACvC,CACV,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,UAAU,EAAE,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { NavAppClickArgs, NavConfigPayload } from './types.js';
3
+ interface NavProviderProps {
4
+ /** Full URL of the public nav-config endpoint. Derived by the host; no new env var. */
5
+ configUrl: string;
6
+ /** The catalog id of the app the host itself is — never inferred from the URL or the payload. */
7
+ activeAppId: string;
8
+ /** Called immediately before navigation. The only analytics seam; the package imports none. */
9
+ onAppClick?: (args: NavAppClickArgs) => void;
10
+ /** Overrides the compiled-in fallback, e.g. for a satellite with a different first-party set. */
11
+ fallbackConfig?: NavConfigPayload;
12
+ /** A server-rendered host passes the collapsed cookie it already read, so the first byte is correct. */
13
+ initialCollapsed?: boolean;
14
+ children?: ReactNode;
15
+ }
16
+ declare function NavProvider({ configUrl, activeAppId, onAppClick, fallbackConfig, initialCollapsed, children, }: NavProviderProps): import("react/jsx-runtime").JSX.Element;
17
+ export { NavProvider };
18
+ export type { NavProviderProps };
19
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA6C,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAMlF,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAkB,MAAM,YAAY,CAAC;AAEpF,UAAU,gBAAgB;IACxB,uFAAuF;IACvF,SAAS,EAAE,MAAM,CAAC;IAClB,iGAAiG;IACjG,WAAW,EAAE,MAAM,CAAC;IACpB,+FAA+F;IAC/F,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAC;IAC7C,iGAAiG;IACjG,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAClC,wGAAwG;IACxG,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,iBAAS,WAAW,CAAC,EACnB,SAAS,EACT,WAAW,EACX,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,QAAQ,GACT,EAAE,gBAAgB,2CAiDlB;AAED,OAAO,EAAE,WAAW,EAAE,CAAC;AACvB,YAAY,EAAE,gBAAgB,EAAE,CAAC"}
@@ -0,0 +1,46 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useCallback, useEffect, useMemo, useState } from 'react';
4
+ import { cached, fetchConfig } from './config.js';
5
+ import { NavContext } from './context.js';
6
+ import { readCollapsed, writeCollapsed } from './cookie.js';
7
+ import { FALLBACK_CONFIG } from './fallback.js';
8
+ import { resolveApps } from './resolve.js';
9
+ function NavProvider({ configUrl, activeAppId, onAppClick, fallbackConfig, initialCollapsed, children, }) {
10
+ const fallback = fallbackConfig ?? FALLBACK_CONFIG;
11
+ // Stale-while-revalidate: paint the last good payload (or the embedded fallback)
12
+ // synchronously, then swap in the fetched one. First paint never waits on the network.
13
+ const [config, setConfig] = useState(() => cached(configUrl) ?? fallback);
14
+ const [collapsed, setCollapsedState] = useState(() => initialCollapsed ?? readCollapsed() ?? false);
15
+ useEffect(() => {
16
+ let live = true;
17
+ void fetchConfig(configUrl).then((next) => {
18
+ if (!live || !next) {
19
+ return;
20
+ }
21
+ setConfig(next);
22
+ });
23
+ return () => {
24
+ live = false;
25
+ };
26
+ }, [configUrl]);
27
+ const setCollapsed = useCallback((next) => {
28
+ setCollapsedState(next);
29
+ writeCollapsed(next);
30
+ }, []);
31
+ const toggle = useCallback(() => {
32
+ setCollapsedState((current) => {
33
+ writeCollapsed(!current);
34
+ return !current;
35
+ });
36
+ }, []);
37
+ const apps = useMemo(() => resolveApps(config, activeAppId, fallback), [config, activeAppId, fallback]);
38
+ const activeApp = useMemo(() => apps.find((item) => item.active), [apps]);
39
+ const activate = useCallback((entry) => {
40
+ onAppClick?.({ app: entry.app, activeAppId, collapsed });
41
+ }, [onAppClick, activeAppId, collapsed]);
42
+ const value = useMemo(() => ({ config, activeAppId, apps, activeApp, collapsed, setCollapsed, toggle, activate }), [config, activeAppId, apps, activeApp, collapsed, setCollapsed, toggle, activate]);
43
+ return _jsx(NavContext.Provider, { value: value, children: children });
44
+ }
45
+ export { NavProvider };
46
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import type { NavApp, NavConfigPayload, ResolvedNavApp } from './types.js';
2
+ declare function hasDestination(app: NavApp): boolean;
3
+ /**
4
+ * The one filter, applied identically to the embedded fallback and to the fetched
5
+ * payload: an entry renders only when it is enabled AND has a destination, so an
6
+ * endpoint outage can never paint a dead tile.
7
+ *
8
+ * The host's own app is the single exemption from `enabled` — it is always
9
+ * rendered and indicated, resolved from the fallback when the payload omits it,
10
+ * because an app that hid itself from its own bar would leave the artist with no
11
+ * indication of where they are and NavTrigger with no chip to dock. It is never
12
+ * exempt from the destination requirement: without one it becomes an inert
13
+ * indicator chip rather than a broken link.
14
+ *
15
+ * A LEGITIMATELY EMPTY payload (`apps: []`, which `normalize` now honours instead of
16
+ * discarding) changes nothing here, deliberately. The empty array is a statement about
17
+ * the CATALOG — the set of other apps a viewer may switch to — and the host's own entry
18
+ * has never been part of that set; it is the one exemption from it. Both invariants
19
+ * therefore hold at once: the catalog is empty, and the viewer still sees where they
20
+ * are and NavTrigger still has a chip to dock. Dropping the active entry too would
21
+ * trade a real regression (no orientation, a blank docked chip) for no gain, and the
22
+ * resolver cannot even see the distinction — an empty fetched payload and an empty
23
+ * `fallbackConfig` arrive here identically.
24
+ *
25
+ * Note this is not a licence to invent destinations: only the ids the compiled-in
26
+ * FALLBACK_CONFIG actually carries (today just `home`) can be resolved this way, which
27
+ * is why an empty payload leaves a satellite host's bar genuinely empty rather than
28
+ * guessing at a domain that may not exist on the stage yet.
29
+ */
30
+ declare function resolveApps(config: NavConfigPayload, activeAppId: string, fallback: NavConfigPayload): ResolvedNavApp[];
31
+ export { hasDestination, resolveApps };
32
+ //# sourceMappingURL=resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE3E,iBAAS,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE5C;AAUD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,iBAAS,WAAW,CAAC,MAAM,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,cAAc,EAAE,CAYhH;AAED,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,51 @@
1
+ import { FALLBACK_CONFIG } from './fallback.js';
2
+ function hasDestination(app) {
3
+ return app.href.trim().length > 0;
4
+ }
5
+ function entry(app, activeAppId) {
6
+ return { app, active: app.id === activeAppId, navigable: hasDestination(app) };
7
+ }
8
+ function findApp(apps, id) {
9
+ return apps.find((app) => app.id === id);
10
+ }
11
+ /**
12
+ * The one filter, applied identically to the embedded fallback and to the fetched
13
+ * payload: an entry renders only when it is enabled AND has a destination, so an
14
+ * endpoint outage can never paint a dead tile.
15
+ *
16
+ * The host's own app is the single exemption from `enabled` — it is always
17
+ * rendered and indicated, resolved from the fallback when the payload omits it,
18
+ * because an app that hid itself from its own bar would leave the artist with no
19
+ * indication of where they are and NavTrigger with no chip to dock. It is never
20
+ * exempt from the destination requirement: without one it becomes an inert
21
+ * indicator chip rather than a broken link.
22
+ *
23
+ * A LEGITIMATELY EMPTY payload (`apps: []`, which `normalize` now honours instead of
24
+ * discarding) changes nothing here, deliberately. The empty array is a statement about
25
+ * the CATALOG — the set of other apps a viewer may switch to — and the host's own entry
26
+ * has never been part of that set; it is the one exemption from it. Both invariants
27
+ * therefore hold at once: the catalog is empty, and the viewer still sees where they
28
+ * are and NavTrigger still has a chip to dock. Dropping the active entry too would
29
+ * trade a real regression (no orientation, a blank docked chip) for no gain, and the
30
+ * resolver cannot even see the distinction — an empty fetched payload and an empty
31
+ * `fallbackConfig` arrive here identically.
32
+ *
33
+ * Note this is not a licence to invent destinations: only the ids the compiled-in
34
+ * FALLBACK_CONFIG actually carries (today just `home`) can be resolved this way, which
35
+ * is why an empty payload leaves a satellite host's bar genuinely empty rather than
36
+ * guessing at a domain that may not exist on the stage yet.
37
+ */
38
+ function resolveApps(config, activeAppId, fallback) {
39
+ const visible = config.apps.filter((app) => app.id === activeAppId || (app.enabled && hasDestination(app)));
40
+ const resolved = visible.map((app) => entry(app, activeAppId));
41
+ if (resolved.some((item) => item.active)) {
42
+ return resolved;
43
+ }
44
+ const embedded = findApp(fallback.apps, activeAppId) ?? findApp(FALLBACK_CONFIG.apps, activeAppId);
45
+ if (!embedded) {
46
+ return resolved;
47
+ }
48
+ return [entry(embedded, activeAppId), ...resolved];
49
+ }
50
+ export { hasDestination, resolveApps };
51
+ //# sourceMappingURL=resolve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAGhD,SAAS,cAAc,CAAC,GAAW;IACjC,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,KAAK,CAAC,GAAW,EAAE,WAAmB;IAC7C,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,WAAW,EAAE,SAAS,EAAE,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC;AACjF,CAAC;AAED,SAAS,OAAO,CAAC,IAAc,EAAE,EAAU;IACzC,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,SAAS,WAAW,CAAC,MAAwB,EAAE,WAAmB,EAAE,QAA0B;IAC5F,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,WAAW,IAAI,CAAC,GAAG,CAAC,OAAO,IAAI,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC5G,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC;IAC/D,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACzC,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,OAAO,CAAC,eAAe,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IACnG,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,WAAW,CAAC,EAAE,GAAG,QAAQ,CAAC,CAAC;AACrD,CAAC;AAED,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { CSSProperties } from 'react';
2
+ import type { NavTheme } from './types.js';
3
+ /**
4
+ * The palettes themselves live in index.css, keyed off data-theme, so switching
5
+ * mode is a config edit rather than a package release. Only the two tokens CSS
6
+ * cannot know in advance come through inline.
7
+ *
8
+ * accent_color replaces the active chip's two-stop gradient with a single flat
9
+ * fill: one value cannot define two stops, so nothing is derived from it.
10
+ */
11
+ declare function themeVars(theme: NavTheme): CSSProperties;
12
+ export { themeVars };
13
+ //# sourceMappingURL=theme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAC3C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;;;;;;GAOG;AACH,iBAAS,SAAS,CAAC,KAAK,EAAE,QAAQ,GAAG,aAAa,CASjD;AAED,OAAO,EAAE,SAAS,EAAE,CAAC"}
package/dist/theme.js ADDED
@@ -0,0 +1,21 @@
1
+ 'use client';
2
+ /**
3
+ * The palettes themselves live in index.css, keyed off data-theme, so switching
4
+ * mode is a config edit rather than a package release. Only the two tokens CSS
5
+ * cannot know in advance come through inline.
6
+ *
7
+ * accent_color replaces the active chip's two-stop gradient with a single flat
8
+ * fill: one value cannot define two stops, so nothing is derived from it.
9
+ */
10
+ function themeVars(theme) {
11
+ const vars = {
12
+ '--ahsuite-tiles-justify': theme.alignment === 'centered' ? 'center' : 'flex-start',
13
+ };
14
+ if (theme.accent_color) {
15
+ vars['--ahsuite-chip-active-bg'] = theme.accent_color;
16
+ vars['--ahsuite-chip-active-image'] = 'none';
17
+ }
18
+ return vars;
19
+ }
20
+ export { themeVars };
21
+ //# sourceMappingURL=theme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.js","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAKb;;;;;;;GAOG;AACH,SAAS,SAAS,CAAC,KAAe;IAChC,MAAM,IAAI,GAA2B;QACnC,yBAAyB,EAAE,KAAK,CAAC,SAAS,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY;KACpF,CAAC;IACF,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;QACvB,IAAI,CAAC,0BAA0B,CAAC,GAAG,KAAK,CAAC,YAAY,CAAC;QACtD,IAAI,CAAC,6BAA6B,CAAC,GAAG,MAAM,CAAC;IAC/C,CAAC;IACD,OAAO,IAAqB,CAAC;AAC/B,CAAC;AAED,OAAO,EAAE,SAAS,EAAE,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * The wire shape of GET /rest/nav-config.
3
+ *
4
+ * The contract is additive: within a schema_version fields are only ever added,
5
+ * never removed or repurposed. Renderers ignore fields they do not recognise and
6
+ * degrade rather than throw on a higher schema_version, so an asynchronously
7
+ * deployed fleet can run mixed renderer versions against one payload.
8
+ */
9
+ type NavThemeMode = 'dark' | 'light';
10
+ type NavAlignment = 'left' | 'centered';
11
+ interface NavTheme {
12
+ mode: NavThemeMode;
13
+ accent_color: string | null;
14
+ alignment: NavAlignment;
15
+ }
16
+ interface NavLogo {
17
+ label: string;
18
+ href: string;
19
+ }
20
+ interface NavApp {
21
+ id: string;
22
+ label: string;
23
+ icon: string;
24
+ href: string;
25
+ enabled: boolean;
26
+ }
27
+ interface NavConfigPayload {
28
+ schema_version: number;
29
+ theme: NavTheme;
30
+ logo: NavLogo;
31
+ apps: NavApp[];
32
+ }
33
+ /**
34
+ * One entry as the renderer sees it. `navigable` is false for the host's own app
35
+ * when the catalog gives it no destination: it still renders and is still
36
+ * indicated, but as an inert chip rather than a broken link.
37
+ */
38
+ interface ResolvedNavApp {
39
+ app: NavApp;
40
+ active: boolean;
41
+ navigable: boolean;
42
+ }
43
+ /** The single seam a host's analytics hooks into. Fired immediately before navigation. */
44
+ interface NavAppClickArgs {
45
+ app: NavApp;
46
+ activeAppId: string;
47
+ collapsed: boolean;
48
+ }
49
+ export type { NavAlignment, NavApp, NavAppClickArgs, NavConfigPayload, NavLogo, NavTheme, NavThemeMode, ResolvedNavApp, };
50
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,KAAK,YAAY,GAAG,MAAM,GAAG,OAAO,CAAC;AAErC,KAAK,YAAY,GAAG,MAAM,GAAG,UAAU,CAAC;AAExC,UAAU,QAAQ;IAChB,IAAI,EAAE,YAAY,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,YAAY,CAAC;CACzB;AAED,UAAU,OAAO;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,MAAM;IACd,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,UAAU,gBAAgB;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,QAAQ,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED;;;;GAIG;AACH,UAAU,cAAc;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,0FAA0F;AAC1F,UAAU,eAAe;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,YAAY,EACV,YAAY,EACZ,MAAM,EACN,eAAe,EACf,gBAAgB,EAChB,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,cAAc,GACf,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The wire shape of GET /rest/nav-config.
3
+ *
4
+ * The contract is additive: within a schema_version fields are only ever added,
5
+ * never removed or repurposed. Renderers ignore fields they do not recognise and
6
+ * degrade rather than throw on a higher schema_version, so an asynchronously
7
+ * deployed fleet can run mixed renderer versions against one payload.
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@artstorefronts/arthelper-nav",
3
+ "version": "0.1.0-alpha.0",
4
+ "description": "The ArtHelper suite navigation bar — a config-driven, cross-origin app switcher.",
5
+ "license": "ISC",
6
+ "type": "module",
7
+ "main": "dist/index.js",
8
+ "module": "dist/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "files": [
11
+ "/dist"
12
+ ],
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "scripts": {
17
+ "build": "tsc && node --import tsx build.ts",
18
+ "test": "vitest --watch=false"
19
+ },
20
+ "peerDependencies": {
21
+ "lucide-react": ">=0.446.0",
22
+ "react": "^18.0.0 || ^19.0.0",
23
+ "react-dom": "^18.0.0 || ^19.0.0"
24
+ },
25
+ "devDependencies": {
26
+ "@testing-library/dom": "^10.4.0",
27
+ "@testing-library/react": "^16.3.2",
28
+ "@types/react": "^19",
29
+ "@types/react-dom": "^19",
30
+ "jsdom": "^26.1.0",
31
+ "lucide-react": "^0.446.0",
32
+ "react": "19.1.2",
33
+ "react-dom": "19.1.2",
34
+ "tsx": "^4.0.0",
35
+ "typescript": "^5.6.2",
36
+ "vitest": "^3.2.4"
37
+ }
38
+ }