@xzibit/ui 0.1.0 → 0.2.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.
- package/CHANGELOG.md +44 -1
- package/README.md +10 -1
- package/dist/index.cjs +33 -1
- package/dist/index.d.cts +85 -7
- package/dist/index.d.ts +85 -7
- package/dist/index.js +31 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,50 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@xzibit/ui` are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/) loosely; versioning follows [SemVer](https://semver.org/).
|
|
4
4
|
|
|
5
|
-
## [0.
|
|
5
|
+
## [0.2.0] — 2026-05-24
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **`<ContentContainer tier="...">`** — implements DESIGN-STANDARD v2.4 §Content Density Tiers. Three tiers: `'editorial'` (720px), `'reference'` (1200px, DEFAULT), `'data'` (unconstrained with 32px side padding). Drop-in wrapper for `<main>` content. Per-page overrides via `tier` prop.
|
|
10
|
+
- **`ContentTier`** type exported for app authors wanting to type tier props at their own layer.
|
|
11
|
+
|
|
12
|
+
### Coming next
|
|
13
|
+
|
|
14
|
+
- `<Toast />` + `useToast()` — wrapper around `sonner` per DESIGN-STANDARD §Toast / Notification (v2.2). Will ship as v0.2.1 once drafted.
|
|
15
|
+
- `<Modal />` — wrapper around `@radix-ui/react-dialog` per DESIGN-STANDARD §Modal / Dialog (v2.2). Will ship as v0.2.2 once drafted.
|
|
16
|
+
|
|
17
|
+
(Joel chose Option A 2026-05-24 — fast-ship v0.2.0 with just ContentContainer to unblock portfolio-wide adoption of the new content tier system. Toast + Modal ship as subsequent patches.)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## [0.1.1] — 2026-05-24
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
- **Peer dependency widened** to accept React 19 (`react@"^18.0.0 || ^19.0.0"` and `react-dom` same). Surfaced during ERP Overview migration — `--legacy-peer-deps` was needed to install on the React 19 app. Affects every React 19 app planning to adopt; v0.1.0 blocked clean install.
|
|
25
|
+
- **API field-name contract resilience.** `useApps` now normalizes raw Supabase column names (`app_url`, `display_section`, `display_order`) to the canonical `App` shape (`url`, `section`, `section_order`) via internal `normalizeApp()` at the fetch boundary. Apps with existing endpoints returning raw query rows now work without changes; new endpoints can return either shape. Surfaced during ERP Overview migration — package rendered empty dropdown until CC added a manual `.map()` normalization in the route handler.
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
- `RawApp` type exported for apps that want to type their endpoint response explicitly
|
|
29
|
+
- `normalizeApp(raw: RawApp): App` helper exported for apps that want to call it directly (rare; `useApps` calls it automatically)
|
|
30
|
+
|
|
31
|
+
### Documentation
|
|
32
|
+
- README's `/api/me/apps` section now documents both accepted field-name conventions and the normalization behaviour.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## [0.1.0] — 2026-05-22 (drafted) / 2026-05-23 (published)
|
|
37
|
+
|
|
38
|
+
### Published artifact
|
|
39
|
+
- npm: https://www.npmjs.com/package/@xzibit/ui
|
|
40
|
+
- Provenance attestation: present
|
|
41
|
+
- Install: `npm install @xzibit/ui`
|
|
42
|
+
|
|
43
|
+
### Bug fixes applied between draft and publish (sync'd back into this draft 2026-05-23)
|
|
44
|
+
- **`package-lock.json` generated** via `npm install` (not present in initial draft — required for `npm ci` in CI).
|
|
45
|
+
- **Removed stale `import React` namespace imports** from 4 source files (`TopBar.tsx`, `XzibitMark.tsx`, `BackToLauncher.tsx`, `AppsDropdown.tsx`). The package's `tsconfig.json` uses `"jsx": "react-jsx"` (modern transform, doesn't need React import) + `noUnusedLocals: true`, so bare `import React from 'react'` fails the DTS build. Fixed by removing the unused namespace import — named imports (`useState`, `useEffect`, etc.) where actually needed are kept.
|
|
46
|
+
|
|
47
|
+
### Future-package note
|
|
48
|
+
When drafting any future React component package for the `@xzibit/*` scope, omit `import React from 'react'` unless React is actually used directly (e.g. `React.forwardRef`). Just import the hooks / types you need by name.
|
|
6
49
|
|
|
7
50
|
### Added
|
|
8
51
|
|
package/README.md
CHANGED
|
@@ -58,7 +58,15 @@ That's it. The bar renders:
|
|
|
58
58
|
|
|
59
59
|
### `/api/me/apps` endpoint required
|
|
60
60
|
|
|
61
|
-
`AppsDropdown` calls `GET /api/me/apps` (same-origin) for the cross-app navigation list. Each consuming app must expose this endpoint per CODING-STANDARDS §6.3
|
|
61
|
+
`AppsDropdown` calls `GET /api/me/apps` (same-origin) for the cross-app navigation list. Each consuming app must expose this endpoint per CODING-STANDARDS §6.3.
|
|
62
|
+
|
|
63
|
+
**Field-name contract (v0.1.1+):** the endpoint can return EITHER:
|
|
64
|
+
- **Canonical shape** (recommended for new endpoints): `{ name, url, description?, section?, section_order? }`
|
|
65
|
+
- **Supabase column-name passthrough** (for endpoints that return raw `public.apps` rows): `{ name, app_url, description?, display_section?, display_order? }`
|
|
66
|
+
|
|
67
|
+
`useApps` normalizes both shapes to the canonical `App` interface at the fetch boundary — components downstream never see the raw form. Apps with existing endpoints that return raw column names work without changes.
|
|
68
|
+
|
|
69
|
+
Example route handler:
|
|
62
70
|
|
|
63
71
|
```typescript
|
|
64
72
|
// src/app/api/me/apps/route.ts
|
|
@@ -138,6 +146,7 @@ This sets `--xz-charcoal`, `--xz-teal`, `--xz-white`, `--border`, etc. — and `
|
|
|
138
146
|
| `<AppsDropdown />` | Sectioned + alphabetical apps dropdown driven by `/api/me/apps` |
|
|
139
147
|
| `<XzibitMark size={28} />` | Xzibit X brand mark as inline SVG (any size, any density) |
|
|
140
148
|
| `useApps()` | React hook fetching `/api/me/apps` with loading + error + refetch |
|
|
149
|
+
| `<ContentContainer tier="reference">` *(v0.2+)* | Content max-width container per DESIGN-STANDARD v2.4 §Content Density Tiers — tiers: `'editorial'` (720px), `'reference'` (1200px, default), `'data'` (unconstrained). Wraps `<main>` content. |
|
|
141
150
|
|
|
142
151
|
---
|
|
143
152
|
|
package/dist/index.cjs
CHANGED
|
@@ -22,8 +22,10 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
AppsDropdown: () => AppsDropdown,
|
|
24
24
|
BackToLauncher: () => BackToLauncher,
|
|
25
|
+
ContentContainer: () => ContentContainer,
|
|
25
26
|
TopBar: () => TopBar,
|
|
26
27
|
XzibitMark: () => XzibitMark,
|
|
28
|
+
normalizeApp: () => normalizeApp,
|
|
27
29
|
useApps: () => useApps
|
|
28
30
|
});
|
|
29
31
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -133,6 +135,19 @@ var import_react3 = require("react");
|
|
|
133
135
|
|
|
134
136
|
// src/useApps.ts
|
|
135
137
|
var import_react2 = require("react");
|
|
138
|
+
|
|
139
|
+
// src/types.ts
|
|
140
|
+
function normalizeApp(raw) {
|
|
141
|
+
return {
|
|
142
|
+
name: raw.name,
|
|
143
|
+
url: raw.url ?? raw.app_url ?? "",
|
|
144
|
+
description: raw.description,
|
|
145
|
+
section: raw.section ?? raw.display_section ?? null,
|
|
146
|
+
section_order: raw.section_order ?? raw.display_order
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// src/useApps.ts
|
|
136
151
|
function useApps(options = {}) {
|
|
137
152
|
const { endpoint = "/api/me/apps", lazy = false } = options;
|
|
138
153
|
const [apps, setApps] = (0, import_react2.useState)([]);
|
|
@@ -153,7 +168,7 @@ function useApps(options = {}) {
|
|
|
153
168
|
if (data.error) {
|
|
154
169
|
throw new Error(data.error);
|
|
155
170
|
}
|
|
156
|
-
setApps(data.apps ?? []);
|
|
171
|
+
setApps((data.apps ?? []).map(normalizeApp));
|
|
157
172
|
} catch (err) {
|
|
158
173
|
console.error("[@xzibit/ui useApps] fetch failed:", err);
|
|
159
174
|
setError(err instanceof Error ? err.message : "Unknown error");
|
|
@@ -545,11 +560,28 @@ function BuildBadge({
|
|
|
545
560
|
}
|
|
546
561
|
);
|
|
547
562
|
}
|
|
563
|
+
|
|
564
|
+
// src/ContentContainer.tsx
|
|
565
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
566
|
+
var TIER_STYLES = {
|
|
567
|
+
editorial: { maxWidth: 720, margin: "0 auto", padding: "3rem 2rem" },
|
|
568
|
+
reference: { maxWidth: 1200, margin: "0 auto", padding: "3rem 2rem" },
|
|
569
|
+
data: { maxWidth: "100%", margin: "0 auto", padding: "1.5rem 2rem" }
|
|
570
|
+
};
|
|
571
|
+
function ContentContainer({
|
|
572
|
+
tier = "reference",
|
|
573
|
+
className,
|
|
574
|
+
children
|
|
575
|
+
}) {
|
|
576
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className, style: TIER_STYLES[tier], children });
|
|
577
|
+
}
|
|
548
578
|
// Annotate the CommonJS export names for ESM import in node:
|
|
549
579
|
0 && (module.exports = {
|
|
550
580
|
AppsDropdown,
|
|
551
581
|
BackToLauncher,
|
|
582
|
+
ContentContainer,
|
|
552
583
|
TopBar,
|
|
553
584
|
XzibitMark,
|
|
585
|
+
normalizeApp,
|
|
554
586
|
useApps
|
|
555
587
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
2
3
|
|
|
3
4
|
interface TopBarProps {
|
|
4
5
|
/** App display name shown in the wordmark slot (e.g. "ERP Overview"). */
|
|
@@ -95,11 +96,12 @@ declare function XzibitMark({ size, className, ariaLabel }: XzibitMarkProps): re
|
|
|
95
96
|
* Shared types for @xzibit/ui.
|
|
96
97
|
*/
|
|
97
98
|
/**
|
|
98
|
-
*
|
|
99
|
+
* Canonical app shape consumed by `@xzibit/ui` components.
|
|
99
100
|
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
101
|
+
* This is what `useApps` returns after normalization. App authors writing
|
|
102
|
+
* NEW `/api/me/apps` endpoints SHOULD return this shape directly. Apps with
|
|
103
|
+
* existing endpoints that return raw Supabase column names (see `RawApp` below)
|
|
104
|
+
* are also accepted — `useApps` normalizes at the fetch boundary.
|
|
103
105
|
*/
|
|
104
106
|
interface App {
|
|
105
107
|
/** Display name, e.g. "Capacity Planner". */
|
|
@@ -113,16 +115,46 @@ interface App {
|
|
|
113
115
|
/** Sort order for the section itself (matches launcher curation). */
|
|
114
116
|
section_order?: number;
|
|
115
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Raw shape accepted from `/api/me/apps` endpoints.
|
|
120
|
+
*
|
|
121
|
+
* Supports two field-naming conventions:
|
|
122
|
+
* - **Canonical** (recommended for new endpoints): `url`, `section`, `section_order`
|
|
123
|
+
* - **Supabase column-name passthrough** (for endpoints that return raw query rows):
|
|
124
|
+
* `app_url`, `display_section`, `display_order`
|
|
125
|
+
*
|
|
126
|
+
* `useApps` normalizes to the canonical `App` shape via `normalizeApp()` at the
|
|
127
|
+
* fetch boundary — components downstream only see the canonical shape.
|
|
128
|
+
*
|
|
129
|
+
* Added in v0.1.1 (2026-05-24) after ERP Overview migration surfaced the contract
|
|
130
|
+
* mismatch with raw Supabase column names.
|
|
131
|
+
*/
|
|
132
|
+
interface RawApp {
|
|
133
|
+
name: string;
|
|
134
|
+
url?: string;
|
|
135
|
+
app_url?: string;
|
|
136
|
+
description?: string;
|
|
137
|
+
section?: string | null;
|
|
138
|
+
display_section?: string | null;
|
|
139
|
+
section_order?: number;
|
|
140
|
+
display_order?: number;
|
|
141
|
+
}
|
|
116
142
|
/**
|
|
117
143
|
* Response shape from `/api/me/apps`.
|
|
118
144
|
*
|
|
119
145
|
* Per CODING-STANDARDS §6.4 — successful responses are wrapped in a `data`
|
|
120
|
-
* or domain-specific key (in this case `apps`).
|
|
146
|
+
* or domain-specific key (in this case `apps`). The `apps` array contains
|
|
147
|
+
* `RawApp` items; `useApps` normalizes them to `App`.
|
|
121
148
|
*/
|
|
122
149
|
interface AppsResponse {
|
|
123
|
-
apps?:
|
|
150
|
+
apps?: RawApp[];
|
|
124
151
|
error?: string;
|
|
125
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* Internal helper — normalizes a `RawApp` to the canonical `App` shape.
|
|
155
|
+
* Used by `useApps` at the fetch boundary; not typically called by consumers.
|
|
156
|
+
*/
|
|
157
|
+
declare function normalizeApp(raw: RawApp): App;
|
|
126
158
|
|
|
127
159
|
interface UseAppsResult {
|
|
128
160
|
apps: App[];
|
|
@@ -150,4 +182,50 @@ interface UseAppsOptions {
|
|
|
150
182
|
*/
|
|
151
183
|
declare function useApps(options?: UseAppsOptions): UseAppsResult;
|
|
152
184
|
|
|
153
|
-
|
|
185
|
+
/**
|
|
186
|
+
* Content density tier per DESIGN-STANDARD v2.4 §Content Density Tiers.
|
|
187
|
+
*
|
|
188
|
+
* - `'editorial'`: 720px max-width — marketing / public-facing docs / long-form prose
|
|
189
|
+
* - `'reference'` (DEFAULT): 1200px max-width — internal docs / wikis / dashboards / most app pages
|
|
190
|
+
* - `'data'`: unconstrained with 32px min side padding — wide tables / kanban / calendars / heatmaps
|
|
191
|
+
*/
|
|
192
|
+
type ContentTier = 'editorial' | 'reference' | 'data';
|
|
193
|
+
interface ContentContainerProps {
|
|
194
|
+
/**
|
|
195
|
+
* Content density tier — picks max-width + padding.
|
|
196
|
+
* Defaults to `'reference'` (the portfolio default per v2.4).
|
|
197
|
+
*/
|
|
198
|
+
tier?: ContentTier;
|
|
199
|
+
/** Optional className for additional styling. */
|
|
200
|
+
className?: string;
|
|
201
|
+
/** Children to render inside the container. */
|
|
202
|
+
children: ReactNode;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Content container that applies the right max-width + padding for its content
|
|
206
|
+
* density tier per DESIGN-STANDARD v2.4 §Content Density Tiers.
|
|
207
|
+
*
|
|
208
|
+
* Per the spec:
|
|
209
|
+
* - Component override > page override > app default (most-specific wins)
|
|
210
|
+
* - Cards INHERIT the parent container's max-width — they do NOT set their own
|
|
211
|
+
*
|
|
212
|
+
* Typical app usage in `src/app/layout.tsx`:
|
|
213
|
+
*
|
|
214
|
+
* ```tsx
|
|
215
|
+
* <main id="main" style={{ marginTop: 44 }}>
|
|
216
|
+
* <ContentContainer>{children}</ContentContainer>
|
|
217
|
+
* </main>
|
|
218
|
+
* ```
|
|
219
|
+
*
|
|
220
|
+
* Per-page override (rare):
|
|
221
|
+
*
|
|
222
|
+
* ```tsx
|
|
223
|
+
* // A wide-table page within an otherwise-reference app
|
|
224
|
+
* <ContentContainer tier="data">
|
|
225
|
+
* <DataTable ... />
|
|
226
|
+
* </ContentContainer>
|
|
227
|
+
* ```
|
|
228
|
+
*/
|
|
229
|
+
declare function ContentContainer({ tier, className, children, }: ContentContainerProps): react_jsx_runtime.JSX.Element;
|
|
230
|
+
|
|
231
|
+
export { type App, AppsDropdown, type AppsDropdownProps, type AppsResponse, BackToLauncher, type BackToLauncherProps, ContentContainer, type ContentContainerProps, type ContentTier, type RawApp, TopBar, type TopBarProps, type UseAppsOptions, type UseAppsResult, XzibitMark, type XzibitMarkProps, normalizeApp, useApps };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
2
3
|
|
|
3
4
|
interface TopBarProps {
|
|
4
5
|
/** App display name shown in the wordmark slot (e.g. "ERP Overview"). */
|
|
@@ -95,11 +96,12 @@ declare function XzibitMark({ size, className, ariaLabel }: XzibitMarkProps): re
|
|
|
95
96
|
* Shared types for @xzibit/ui.
|
|
96
97
|
*/
|
|
97
98
|
/**
|
|
98
|
-
*
|
|
99
|
+
* Canonical app shape consumed by `@xzibit/ui` components.
|
|
99
100
|
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
101
|
+
* This is what `useApps` returns after normalization. App authors writing
|
|
102
|
+
* NEW `/api/me/apps` endpoints SHOULD return this shape directly. Apps with
|
|
103
|
+
* existing endpoints that return raw Supabase column names (see `RawApp` below)
|
|
104
|
+
* are also accepted — `useApps` normalizes at the fetch boundary.
|
|
103
105
|
*/
|
|
104
106
|
interface App {
|
|
105
107
|
/** Display name, e.g. "Capacity Planner". */
|
|
@@ -113,16 +115,46 @@ interface App {
|
|
|
113
115
|
/** Sort order for the section itself (matches launcher curation). */
|
|
114
116
|
section_order?: number;
|
|
115
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Raw shape accepted from `/api/me/apps` endpoints.
|
|
120
|
+
*
|
|
121
|
+
* Supports two field-naming conventions:
|
|
122
|
+
* - **Canonical** (recommended for new endpoints): `url`, `section`, `section_order`
|
|
123
|
+
* - **Supabase column-name passthrough** (for endpoints that return raw query rows):
|
|
124
|
+
* `app_url`, `display_section`, `display_order`
|
|
125
|
+
*
|
|
126
|
+
* `useApps` normalizes to the canonical `App` shape via `normalizeApp()` at the
|
|
127
|
+
* fetch boundary — components downstream only see the canonical shape.
|
|
128
|
+
*
|
|
129
|
+
* Added in v0.1.1 (2026-05-24) after ERP Overview migration surfaced the contract
|
|
130
|
+
* mismatch with raw Supabase column names.
|
|
131
|
+
*/
|
|
132
|
+
interface RawApp {
|
|
133
|
+
name: string;
|
|
134
|
+
url?: string;
|
|
135
|
+
app_url?: string;
|
|
136
|
+
description?: string;
|
|
137
|
+
section?: string | null;
|
|
138
|
+
display_section?: string | null;
|
|
139
|
+
section_order?: number;
|
|
140
|
+
display_order?: number;
|
|
141
|
+
}
|
|
116
142
|
/**
|
|
117
143
|
* Response shape from `/api/me/apps`.
|
|
118
144
|
*
|
|
119
145
|
* Per CODING-STANDARDS §6.4 — successful responses are wrapped in a `data`
|
|
120
|
-
* or domain-specific key (in this case `apps`).
|
|
146
|
+
* or domain-specific key (in this case `apps`). The `apps` array contains
|
|
147
|
+
* `RawApp` items; `useApps` normalizes them to `App`.
|
|
121
148
|
*/
|
|
122
149
|
interface AppsResponse {
|
|
123
|
-
apps?:
|
|
150
|
+
apps?: RawApp[];
|
|
124
151
|
error?: string;
|
|
125
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* Internal helper — normalizes a `RawApp` to the canonical `App` shape.
|
|
155
|
+
* Used by `useApps` at the fetch boundary; not typically called by consumers.
|
|
156
|
+
*/
|
|
157
|
+
declare function normalizeApp(raw: RawApp): App;
|
|
126
158
|
|
|
127
159
|
interface UseAppsResult {
|
|
128
160
|
apps: App[];
|
|
@@ -150,4 +182,50 @@ interface UseAppsOptions {
|
|
|
150
182
|
*/
|
|
151
183
|
declare function useApps(options?: UseAppsOptions): UseAppsResult;
|
|
152
184
|
|
|
153
|
-
|
|
185
|
+
/**
|
|
186
|
+
* Content density tier per DESIGN-STANDARD v2.4 §Content Density Tiers.
|
|
187
|
+
*
|
|
188
|
+
* - `'editorial'`: 720px max-width — marketing / public-facing docs / long-form prose
|
|
189
|
+
* - `'reference'` (DEFAULT): 1200px max-width — internal docs / wikis / dashboards / most app pages
|
|
190
|
+
* - `'data'`: unconstrained with 32px min side padding — wide tables / kanban / calendars / heatmaps
|
|
191
|
+
*/
|
|
192
|
+
type ContentTier = 'editorial' | 'reference' | 'data';
|
|
193
|
+
interface ContentContainerProps {
|
|
194
|
+
/**
|
|
195
|
+
* Content density tier — picks max-width + padding.
|
|
196
|
+
* Defaults to `'reference'` (the portfolio default per v2.4).
|
|
197
|
+
*/
|
|
198
|
+
tier?: ContentTier;
|
|
199
|
+
/** Optional className for additional styling. */
|
|
200
|
+
className?: string;
|
|
201
|
+
/** Children to render inside the container. */
|
|
202
|
+
children: ReactNode;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Content container that applies the right max-width + padding for its content
|
|
206
|
+
* density tier per DESIGN-STANDARD v2.4 §Content Density Tiers.
|
|
207
|
+
*
|
|
208
|
+
* Per the spec:
|
|
209
|
+
* - Component override > page override > app default (most-specific wins)
|
|
210
|
+
* - Cards INHERIT the parent container's max-width — they do NOT set their own
|
|
211
|
+
*
|
|
212
|
+
* Typical app usage in `src/app/layout.tsx`:
|
|
213
|
+
*
|
|
214
|
+
* ```tsx
|
|
215
|
+
* <main id="main" style={{ marginTop: 44 }}>
|
|
216
|
+
* <ContentContainer>{children}</ContentContainer>
|
|
217
|
+
* </main>
|
|
218
|
+
* ```
|
|
219
|
+
*
|
|
220
|
+
* Per-page override (rare):
|
|
221
|
+
*
|
|
222
|
+
* ```tsx
|
|
223
|
+
* // A wide-table page within an otherwise-reference app
|
|
224
|
+
* <ContentContainer tier="data">
|
|
225
|
+
* <DataTable ... />
|
|
226
|
+
* </ContentContainer>
|
|
227
|
+
* ```
|
|
228
|
+
*/
|
|
229
|
+
declare function ContentContainer({ tier, className, children, }: ContentContainerProps): react_jsx_runtime.JSX.Element;
|
|
230
|
+
|
|
231
|
+
export { type App, AppsDropdown, type AppsDropdownProps, type AppsResponse, BackToLauncher, type BackToLauncherProps, ContentContainer, type ContentContainerProps, type ContentTier, type RawApp, TopBar, type TopBarProps, type UseAppsOptions, type UseAppsResult, XzibitMark, type XzibitMarkProps, normalizeApp, useApps };
|
package/dist/index.js
CHANGED
|
@@ -103,6 +103,19 @@ import { useState as useState3, useEffect as useEffect2, useRef, Fragment } from
|
|
|
103
103
|
|
|
104
104
|
// src/useApps.ts
|
|
105
105
|
import { useState as useState2, useEffect, useCallback } from "react";
|
|
106
|
+
|
|
107
|
+
// src/types.ts
|
|
108
|
+
function normalizeApp(raw) {
|
|
109
|
+
return {
|
|
110
|
+
name: raw.name,
|
|
111
|
+
url: raw.url ?? raw.app_url ?? "",
|
|
112
|
+
description: raw.description,
|
|
113
|
+
section: raw.section ?? raw.display_section ?? null,
|
|
114
|
+
section_order: raw.section_order ?? raw.display_order
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// src/useApps.ts
|
|
106
119
|
function useApps(options = {}) {
|
|
107
120
|
const { endpoint = "/api/me/apps", lazy = false } = options;
|
|
108
121
|
const [apps, setApps] = useState2([]);
|
|
@@ -123,7 +136,7 @@ function useApps(options = {}) {
|
|
|
123
136
|
if (data.error) {
|
|
124
137
|
throw new Error(data.error);
|
|
125
138
|
}
|
|
126
|
-
setApps(data.apps ?? []);
|
|
139
|
+
setApps((data.apps ?? []).map(normalizeApp));
|
|
127
140
|
} catch (err) {
|
|
128
141
|
console.error("[@xzibit/ui useApps] fetch failed:", err);
|
|
129
142
|
setError(err instanceof Error ? err.message : "Unknown error");
|
|
@@ -515,10 +528,27 @@ function BuildBadge({
|
|
|
515
528
|
}
|
|
516
529
|
);
|
|
517
530
|
}
|
|
531
|
+
|
|
532
|
+
// src/ContentContainer.tsx
|
|
533
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
534
|
+
var TIER_STYLES = {
|
|
535
|
+
editorial: { maxWidth: 720, margin: "0 auto", padding: "3rem 2rem" },
|
|
536
|
+
reference: { maxWidth: 1200, margin: "0 auto", padding: "3rem 2rem" },
|
|
537
|
+
data: { maxWidth: "100%", margin: "0 auto", padding: "1.5rem 2rem" }
|
|
538
|
+
};
|
|
539
|
+
function ContentContainer({
|
|
540
|
+
tier = "reference",
|
|
541
|
+
className,
|
|
542
|
+
children
|
|
543
|
+
}) {
|
|
544
|
+
return /* @__PURE__ */ jsx5("div", { className, style: TIER_STYLES[tier], children });
|
|
545
|
+
}
|
|
518
546
|
export {
|
|
519
547
|
AppsDropdown,
|
|
520
548
|
BackToLauncher,
|
|
549
|
+
ContentContainer,
|
|
521
550
|
TopBar,
|
|
522
551
|
XzibitMark,
|
|
552
|
+
normalizeApp,
|
|
523
553
|
useApps
|
|
524
554
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xzibit/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Shared chrome components for the Xzibit Apps portfolio. v0.1: TopBar + BackToLauncher + AppsDropdown + XzibitMark + useApps hook. Single source of truth for portfolio chrome — tweak once, every app picks it up on next deploy.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Xzibit Apps",
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"prepublishOnly": "npm run build"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"react": "^18.0.0",
|
|
46
|
-
"react-dom": "^18.0.0"
|
|
45
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
46
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"typescript": "^5.0.0",
|