@sybilion/uilib 1.2.0 → 1.2.3

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 (45) hide show
  1. package/dist/esm/components/ui/AppHeader/AppHeader.js +3 -3
  2. package/dist/esm/components/ui/Image/Image.styl.js +1 -1
  3. package/dist/esm/components/ui/NavUserHeader/NavUserHeader.js +28 -0
  4. package/dist/esm/components/ui/NavUserHeader/NavUserHeader.styl.js +7 -0
  5. package/dist/esm/components/ui/Page/AppShell/AppShell.styl.js +1 -1
  6. package/dist/esm/components/ui/Page/PageScroll/PageScroll.js +4 -4
  7. package/dist/esm/components/widgets/SidebarDatasetsItemsGrouped/SidebarDatasetsItemsGrouped.js +9 -9
  8. package/dist/esm/index.js +2 -1
  9. package/dist/esm/sybilion-auth/SybilionAuthProvider.js +30 -7
  10. package/dist/esm/sybilion-auth/exchangeSybilionToken.js +6 -2
  11. package/dist/esm/types/src/components/ui/AppHeader/AppHeader.d.ts +2 -1
  12. package/dist/esm/types/src/components/ui/NavUserHeader/NavUserHeader.d.ts +2 -0
  13. package/dist/esm/types/src/components/ui/NavUserHeader/NavUserHeader.types.d.ts +25 -0
  14. package/dist/esm/types/src/components/ui/NavUserHeader/index.d.ts +2 -0
  15. package/dist/esm/types/src/components/ui/Page/PageScroll/PageScroll.d.ts +2 -1
  16. package/dist/esm/types/src/components/widgets/SidebarDatasetsItemsGrouped/SidebarDatasetsItemsGrouped.d.ts +3 -1
  17. package/dist/esm/types/src/docs/pages/NavUserHeaderPage.d.ts +1 -0
  18. package/dist/esm/types/src/docs/pages/StandaloneAppLayoutPage.d.ts +1 -0
  19. package/dist/esm/types/src/index.d.ts +1 -0
  20. package/dist/esm/types/src/sybilion-auth/SybilionAuthProvider.d.ts +5 -2
  21. package/dist/esm/types/src/sybilion-auth/exchangeSybilionToken.d.ts +3 -1
  22. package/dist/esm/types/src/sybilion-auth/index.d.ts +1 -1
  23. package/docs/standalone-apps.md +266 -27
  24. package/package.json +6 -1
  25. package/src/components/ui/AppHeader/AppHeader.tsx +7 -3
  26. package/src/components/ui/Image/Image.styl +1 -0
  27. package/src/components/ui/NavUserHeader/NavUserHeader.styl +125 -0
  28. package/src/components/ui/NavUserHeader/NavUserHeader.styl.d.ts +28 -0
  29. package/src/components/ui/NavUserHeader/NavUserHeader.tsx +148 -0
  30. package/src/components/ui/NavUserHeader/NavUserHeader.types.ts +27 -0
  31. package/src/components/ui/NavUserHeader/avatar.svg +4 -0
  32. package/src/components/ui/NavUserHeader/index.ts +5 -0
  33. package/src/components/ui/Page/AppShell/AppShell.styl +1 -0
  34. package/src/components/ui/Page/PageScroll/PageScroll.tsx +9 -2
  35. package/src/components/widgets/SidebarDatasetsItemsGrouped/SidebarDatasetsItemsGrouped.tsx +9 -0
  36. package/src/docs/pages/NavUserHeaderPage.tsx +89 -0
  37. package/src/docs/pages/StandaloneAppLayoutPage.styl +46 -0
  38. package/src/docs/pages/StandaloneAppLayoutPage.styl.d.ts +8 -0
  39. package/src/docs/pages/StandaloneAppLayoutPage.tsx +242 -0
  40. package/src/docs/pages/SybilionAuthProviderPage.tsx +5 -2
  41. package/src/docs/registry.ts +12 -0
  42. package/src/index.ts +1 -0
  43. package/src/sybilion-auth/SybilionAuthProvider.tsx +33 -11
  44. package/src/sybilion-auth/exchangeSybilionToken.ts +5 -1
  45. package/src/sybilion-auth/index.ts +1 -0
@@ -1,63 +1,302 @@
1
1
  # Standalone Sybilion apps (@sybilion/uilib)
2
2
 
3
- Greenfield SPA on **your own origin**: `@sybilion/uilib` for layout/UI, `**SybilionAuthProvider`\*\* + Sybilion API for data—no iframe in the main client.
3
+ Greenfield SPA on **your own origin**: `@sybilion/uilib` for layout/UI, `SybilionAuthProvider` + Sybilion API for data—no iframe in the main client.
4
4
 
5
- **Agents / humans:** Use `AppShell` + `AppShellMainContent`; stick to uilib spacing primitives (no random root horizontal gutters).
5
+ **Agents / humans:** use `AppShell` + `AppShellMainContent`; stick to uilib spacing primitives (e.g. `Gap`) instead of ad hoc root horizontal gutters.
6
6
 
7
7
  ## 1. Dependencies and global CSS
8
8
 
9
9
  ```bash
10
- yarn add react react-dom react-router-dom @auth0/auth0-react @sybilion/uilib
10
+ yarn add react react-dom react-router-dom @auth0/auth0-react @sybilion/uilib @sybilion/sdk
11
11
  ```
12
12
 
13
- Import tokens/fonts once:
13
+ Import tokens/fonts once (typically `src/main.tsx`):
14
14
 
15
15
  ```ts
16
16
  import '@sybilion/uilib/standalone-global.css';
17
17
  ```
18
18
 
19
- ## 2. Layout (AppShell)
19
+ Mount the tree with `ReactDOM.createRoot` `App` (wrap with `StrictMode` if you want).
20
20
 
21
- Pattern: `[src/docs/DocsShell.tsx](https://github.com/Mir-Insight/uilib/blob/main/src/docs/DocsShell.tsx)` `PageScroll` → `AppShell` → sidebar → `AppShellMainContent` with `AppHeaderHost`, `PageFooter`, main content (`Outlet` / routes). Add `Theme` and optional `SidebarProvider` from `@homecode/ui`.
21
+ ## 2. SDK (`@sybilion/sdk`)
22
+
23
+ Typed HTTP client for the Sybilion API. Env vars depend on bundler; for Vite, only `import.meta.env.VITE_*` reaches the client.
24
+
25
+ Create **one** instance at module scope (e.g. `src/libs/sybilion-sdk.ts`) so React never reconstructs the client (`useMemo` not needed), and reuse it for auth + data:
26
+
27
+ ```ts
28
+ import { createSybilionSDK } from '@sybilion/sdk';
29
+
30
+ export const sybilionJwtStorageKey = 'sybilion.standalone.jwt';
31
+
32
+ export const sybilionSdk = createSybilionSDK({
33
+ baseUrl: import.meta.env.VITE_SYBILION_API_BASE_URL as string,
34
+ apiPrefix: '/api',
35
+ getToken: () =>
36
+ typeof localStorage !== 'undefined'
37
+ ? (localStorage.getItem(sybilionJwtStorageKey) ?? undefined)
38
+ : undefined,
39
+ });
40
+ ```
41
+
42
+ **Options:** `baseUrl` — API origin only (no trailing slash). `apiPrefix` — default `'/api'` so calls go to `{baseUrl}/api/v1/...`. `getToken` — must read the same key you pass as `sybilionTokenStorageKey` on `SybilionAuthProvider` (§3).
43
+
44
+ ```ts
45
+ import { sybilionSdk } from './libs/sybilion-sdk';
46
+
47
+ await sybilionSdk.raw.datasets.getById(datasetId);
48
+ ```
49
+
50
+ - `sybilionSdk.auth` — `loginWithAuth0Identity`, `getMe`, `updateMe` (Auth0 bootstrap + user profile).
51
+ - `sybilionSdk.raw` — thin `GET`/`POST`/… wrappers for `/v1/...` paths (e.g. `raw.analyses.driversMapOnce`; parsed JSON, no app-specific shaping).
52
+ - `sybilionSdk.resources` — higher-level helpers (datasets, drivers, subscriptions) on top of `raw`.
53
+
54
+ Package README: [`@sybilion/sdk`](https://www.npmjs.com/package/@sybilion/sdk) — monorepo: [`../../sdk/README.md`](../../sdk/README.md).
22
55
 
23
56
  ## 3. Auth (`SybilionAuthProvider`)
24
57
 
25
58
  Use inside `BrowserRouter` if redirects hit a callback route.
26
59
 
27
- Env vars depend on bundlerfor **Vite** only `import.meta.env.VITE_`\* is exposed client-side:
60
+ Wire the SDK module from §2 no second `createSybilionSDK` here:
28
61
 
29
62
  ```tsx
63
+ import type { ReactNode } from 'react';
64
+
30
65
  import { SybilionAuthProvider } from '@sybilion/uilib';
31
66
 
32
- const apiBaseUrl = import.meta.env.VITE_SYBILION_API_BASE_URL as string;
33
- const auth0Domain = import.meta.env.VITE_AUTH0_DOMAIN as string;
34
- const auth0ClientId = import.meta.env.VITE_AUTH0_CLIENT_ID as string;
35
-
36
- <SybilionAuthProvider
37
- apiBaseUrl={apiBaseUrl}
38
- auth0Domain={auth0Domain}
39
- auth0ClientId={auth0ClientId}
40
- redirectUri={window.location.origin}
41
- >
42
- <App />
43
- </SybilionAuthProvider>;
67
+ import { sybilionJwtStorageKey, sybilionSdk } from './libs/sybilion-sdk';
68
+
69
+ export function AppProviders({ children }: { children: ReactNode }) {
70
+ const auth0Domain = import.meta.env.VITE_AUTH0_DOMAIN as string;
71
+ const auth0ClientId = import.meta.env.VITE_AUTH0_CLIENT_ID as string;
72
+
73
+ return (
74
+ <SybilionAuthProvider
75
+ sdk={sybilionSdk}
76
+ sybilionTokenStorageKey={sybilionJwtStorageKey}
77
+ auth0Domain={auth0Domain}
78
+ auth0ClientId={auth0ClientId}
79
+ redirectUri={window.location.origin}
80
+ >
81
+ {children}
82
+ </SybilionAuthProvider>
83
+ );
84
+ }
44
85
  ```
45
86
 
46
- **Flow:** Auth0 SPA → `POST {apiBaseUrl}/v1/auth/login` with `{ identity: "<Auth0 AT>", type: "auth0" }` → Sybilion JWT in response (`data.token` or `token`) → Bearer on API calls.
87
+ **Flow:** Auth0 SPA → `sybilionSdk.auth.loginWithAuth0Identity(<Auth0 AT>)` → Sybilion JWT (`data.token` / `token`) persisted same `sybilionSdk` + `getToken` attach Bearer on requests; `useSybilionApiFetch()` uses `getSybilionApiOriginFromSdk(sdk)` for URLs (paths like `/api/v1/...`).
47
88
 
48
89
  **Defaults** for `authorizationParams` match sybilion-client (Management audience + `openid profile email offline_access …`); override if your Auth0 SPA needs a Resource Server audience (backend confirms).
49
90
 
50
- | Layer | Configure |
51
- | ---------------- | ------------------------------------------------------------------------------------- |
52
- | **Auth0** | Callback, logout, and web origins → your URLs (+ previews). |
53
- | **Sybilion API** | CORS → your deploy `Origin`. |
54
- | **App** | `apiBaseUrl`, Auth0 `domain` / `clientId`, redirect usually `window.location.origin`. |
91
+ | Layer | Configure |
92
+ | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
93
+ | **Auth0** | Callback, logout, and web origins → your URLs (+ previews). |
94
+ | **Sybilion API** | CORS → your deploy `Origin`. |
95
+ | **App** | §2 SDK module (`baseUrl`, `apiPrefix`, `getToken`); pass `sdk` + matching `sybilionTokenStorageKey`; Auth0 `domain` / `clientId`; redirect usually `window.location.origin`. |
55
96
 
56
97
  **Hooks:** `useSybilionAuth()`, `useSybilionApiFetch()` (or `createSybilionApiFetch` / `sybilionApiFetch` helpers).
57
98
 
58
- ## 4. Data
99
+ ## 4. Layout (AppShell)
100
+
101
+ With §2 `sybilionSdk` and §3 `AppProviders` / `SybilionAuthProvider` defined, compose routing + shell so Auth0 callbacks and JWT-backed hooks wrap the whole UI.
102
+
103
+ ### Root wiring (`App.tsx`)
104
+
105
+ Order outside → in: `BrowserRouter` (callbacks) → `AppProviders` (§3) → `SidebarProvider` (sidebar width / open state for `Sidebar` primitives) → `AppLayout` → `Routes`.
106
+
107
+ `AppLayout` renders the persistent chrome (sidebar, header, footer) and the active route renders inside its main column via `children`. Add `HomePage` / `DatasetsPage` as your own page components; register an Auth0 callback route here if `SybilionAuthProvider` uses a dedicated path.
108
+
109
+ ```tsx
110
+ import { BrowserRouter, Route, Routes } from 'react-router-dom';
111
+
112
+ import { SidebarProvider } from '@sybilion/uilib';
113
+
114
+ import { AppLayout } from './AppLayout';
115
+ import { AppProviders } from './AppProviders';
116
+ import { DatasetsPage } from './pages/DatasetsPage';
117
+ import { HomePage } from './pages/HomePage';
118
+
119
+ export function App() {
120
+ return (
121
+ <BrowserRouter>
122
+ <AppProviders>
123
+ <SidebarProvider
124
+ sidebarWidthStorageKey="myapp.sidebarWidthPx"
125
+ persistSidebarWidthWithoutConsent
126
+ >
127
+ <AppLayout>
128
+ <Routes>
129
+ <Route path="/" element={<HomePage />} />
130
+ <Route path="/datasets" element={<DatasetsPage />} />
131
+ </Routes>
132
+ </AppLayout>
133
+ </SidebarProvider>
134
+ </AppProviders>
135
+ </BrowserRouter>
136
+ );
137
+ }
138
+ ```
139
+
140
+ `SidebarProvider`: pass only the props you need. `sidebarWidthStorageKey` namespaces width in localStorage. For production apps with cookie-based consent, drop `persistSidebarWidthWithoutConsent` and pass `userId` so width persistence follows your consent rules.
141
+
142
+ ### `AppLayout` (sidebar + main + `children`)
143
+
144
+ `AppHeaderHost` is the header anchor; put `SidebarTrigger` (collapses / opens rail), `NavUserHeader`, etc. inside `AppHeaderPortal`. `AppSidebar` (next subsection) is a sibling of `AppShellMainContent` inside `AppShell`. The matched route (the `<Routes>` subtree from `App.tsx`) arrives as `children` and renders inside the main column.
145
+
146
+ ```tsx
147
+ import type { ReactNode } from 'react';
148
+
149
+ import {
150
+ AppHeaderHost,
151
+ AppHeaderPortal,
152
+ AppShell,
153
+ AppShellMainContent,
154
+ Gap,
155
+ NavUserHeader,
156
+ PageFooter,
157
+ PageScroll,
158
+ SidebarTrigger,
159
+ } from '@sybilion/uilib';
160
+
161
+ import { AppSidebar } from './AppSidebar';
162
+
163
+ export function AppLayout({ children }: { children: ReactNode }) {
164
+ return (
165
+ <PageScroll>
166
+ <AppShell>
167
+ <AppSidebar />
168
+
169
+ <AppShellMainContent
170
+ header={<AppHeaderHost />}
171
+ footer={<PageFooter versionLink="/releases" versionLabel="0.0.1" />}
172
+ >
173
+ <AppHeaderPortal>
174
+ <SidebarTrigger />
175
+ <Gap />
176
+ <NavUserHeader
177
+ theme="light"
178
+ onThemeToggle={() => undefined}
179
+ onLogout={() => undefined}
180
+ isAuthenticated={false}
181
+ />
182
+ </AppHeaderPortal>
183
+ {children}
184
+ </AppShellMainContent>
185
+ </AppShell>
186
+ </PageScroll>
187
+ );
188
+ }
189
+ ```
190
+
191
+ Wire `NavUserHeader` to real auth (`useSybilionAuth`, theme context, etc.; §3). Demo in repo: `src/docs/pages/NavUserHeaderPage.tsx` (slug `nav-user-header`).
192
+
193
+ #### Sidebar (`AppSidebar.tsx`)
194
+
195
+ App-specific sidebar component — keeps the navigation surface out of `AppLayout` so the shell stays generic. Compose your nav from `@sybilion/uilib` primitives (`Sidebar` + `SidebarContent` + `SidebarGroup` + `SidebarMenu*`) and product widgets like `SidebarDatasetsItemsGrouped` (collapsible groups + nested rows for datasets — see demo `src/docs/pages/SidebarDatasetsItemsGroupedPage.tsx`, slug `sidebar-datasets-items-grouped`).
196
+
197
+ ```tsx
198
+ import { useEffect, useState } from 'react';
199
+ import { NavLink, useNavigate } from 'react-router-dom';
200
+
201
+ import {
202
+ Sidebar,
203
+ SidebarContent,
204
+ SidebarDatasetsItemsGrouped,
205
+ type SidebarDatasetsItemsGroupedDataset,
206
+ SidebarGroup,
207
+ SidebarMenu,
208
+ SidebarMenuButton,
209
+ SidebarMenuItem,
210
+ } from '@sybilion/uilib';
211
+
212
+ import { sybilionSdk } from './libs/sybilion-sdk';
213
+
214
+ export function AppSidebar() {
215
+ const navigate = useNavigate();
216
+ const [datasets, setDatasets] = useState<
217
+ SidebarDatasetsItemsGroupedDataset[]
218
+ >([]);
219
+ const [selectedDatasetId, setSelectedDatasetId] = useState<number>();
220
+
221
+ useEffect(() => {
222
+ sybilionSdk.raw.datasetsIndex(1, 50).then(res => {
223
+ setDatasets(res?.data?.datasets ?? []);
224
+ });
225
+ }, []);
226
+
227
+ return (
228
+ <Sidebar variant="inset" collapsible="offcanvas">
229
+ <SidebarContent>
230
+ <SidebarGroup>
231
+ <SidebarMenu>
232
+ <SidebarMenuItem>
233
+ <SidebarMenuButton asChild>
234
+ <NavLink to="/" end>
235
+ Home
236
+ </NavLink>
237
+ </SidebarMenuButton>
238
+ </SidebarMenuItem>
239
+ <SidebarMenuItem>
240
+ <SidebarMenuButton asChild>
241
+ <NavLink to="/datasets">Datasets</NavLink>
242
+ </SidebarMenuButton>
243
+ </SidebarMenuItem>
244
+ </SidebarMenu>
245
+ </SidebarGroup>
246
+
247
+ <SidebarDatasetsItemsGrouped
248
+ groupBy="regions"
249
+ datasets={datasets}
250
+ selectedDatasetId={selectedDatasetId}
251
+ onDatasetClick={id => {
252
+ setSelectedDatasetId(id);
253
+ navigate(`/datasets/${id}`);
254
+ }}
255
+ />
256
+ </SidebarContent>
257
+ </Sidebar>
258
+ );
259
+ }
260
+ ```
261
+
262
+ Data loading uses §2 `sybilionSdk` directly — for production swap the inline `useEffect` for your data layer (React Query, SWR, context, etc.). `groupBy` accepts `'regions' | 'target_type' | 'categories'`; the widget owns its expand state and notifies on selection via `onDatasetClick`.
263
+
264
+ ### Full pattern
265
+
266
+ Composition: `PageScroll` → `AppShell` → `AppSidebar` → `AppShellMainContent` with `AppHeaderHost`, `PageFooter`, and the active route as `children`. `SidebarProvider` wraps `AppLayout`. Add `Theme` from `@homecode/ui` only when your product uses those primitives alongside uilib.
267
+
268
+ ### Greenfield checklist (agents)
269
+
270
+ | Step | Deliverable |
271
+ | ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
272
+ | Env | `VITE_SYBILION_API_BASE_URL`, `VITE_AUTH0_DOMAIN`, `VITE_AUTH0_CLIENT_ID` (names mirror §2–§3). |
273
+ | Files | `src/libs/sybilion-sdk.ts`, `AppProviders.tsx`, `AppLayout.tsx`, `AppSidebar.tsx`, `App.tsx`, `main.tsx`, route `element` pages under e.g. `src/pages/`. |
274
+ | Auth0 | SPA callback / logout URLs + allowed web origins → your deploy URLs (and previews). |
275
+ | API | Sybilion backend CORS → same `Origin` values. |
276
+
277
+ ### Glossary (high-use pieces)
278
+
279
+ | Component / API | What it is for |
280
+ | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
281
+ | `PageScroll` | Page-level vertical scroll wrapper; usual outer shell for the app body. |
282
+ | `AppShell` | Layout grid container; `Sidebar` + `AppShellMainContent` as siblings inside it. |
283
+ | `AppShellMainContent` | Main column: `header`, scrollable body (`children`), `footer`. |
284
+ | `AppHeaderHost` | Top header anchor (DOM id `page-header`); stays empty until `AppHeaderPortal` portals into it. |
285
+ | `AppHeaderPortal` | Portals into `AppHeaderHost` — `SidebarTrigger`, `NavUserHeader`, theme toggle. |
286
+ | `NavUserHeader` | Header user menu (avatar, account, theme, logout). |
287
+ | `Sidebar`, `SidebarProvider` | Collapsible rail + context (`@sybilion/uilib`). Wrap `SidebarProvider` above `AppLayout`; render `Sidebar` inside `AppShell` (usually via `AppSidebar`). |
288
+ | `AppSidebar` | App-specific component (`src/AppSidebar.tsx`) composing `Sidebar` + nav links + product widgets. Keeps `AppLayout` generic. |
289
+ | `SidebarDatasetsItemsGrouped` | Dataset list widget for the sidebar: collapsible groups (`regions` / `target_type` / `categories`) with nested rows + selection callback. |
290
+ | `SidebarTrigger` | Toggle sidebar visibility (especially mobile / `offcanvas`). |
291
+ | `PageFooter` | Standard footer; requires `versionLink` + `versionLabel`. |
292
+ | `Gap` | Spacing primitive between flex children. |
293
+ | `SybilionAuthProvider` | Auth0 + Sybilion JWT (§3). |
294
+ | `useSybilionAuth` | User session + login/logout under provider. |
295
+ | `useSybilionApiFetch` | Authenticated `fetch` using stored JWT. |
296
+
297
+ ## 5. Data
59
298
 
60
- Fetch Sybilion API with the JWT above—no host `postMessage` bridge.
299
+ Inside `SybilionAuthProvider`, use `useSybilionApiFetch()` for authenticated `fetch`, or import `sybilionSdk` from §2 for `raw` / `resources` — same JWT storage and API origin either way.
61
300
 
62
301
  ## Related
63
302
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sybilion/uilib",
3
- "version": "1.2.0",
3
+ "version": "1.2.3",
4
4
  "description": "Sybilion Design System — React UI components (Webpack + Stylus)",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -106,6 +106,7 @@
106
106
  },
107
107
  "peerDependencies": {
108
108
  "@auth0/auth0-react": "^2.3.1",
109
+ "@sybilion/sdk": ">=0.0.1",
109
110
  "react": ">=18.0.0",
110
111
  "react-dom": ">=18.0.0",
111
112
  "react-router-dom": ">=6.0.0"
@@ -113,10 +114,14 @@
113
114
  "peerDependenciesMeta": {
114
115
  "@auth0/auth0-react": {
115
116
  "optional": true
117
+ },
118
+ "@sybilion/sdk": {
119
+ "optional": true
116
120
  }
117
121
  },
118
122
  "devDependencies": {
119
123
  "@auth0/auth0-react": "^2.3.1",
124
+ "@sybilion/sdk": "file:../sdk",
120
125
  "@babel/core": "^7.20.12",
121
126
  "@babel/preset-typescript": "^7.21.0",
122
127
  "@homecode/ui": "^4.30.6",
@@ -20,14 +20,18 @@ export function AppHeaderHost({
20
20
 
21
21
  export type AppHeaderPortalProps = {
22
22
  children: ReactNode;
23
+ pageHeaderId?: string;
23
24
  };
24
25
 
25
- export function AppHeaderPortal({ children }: AppHeaderPortalProps) {
26
+ export function AppHeaderPortal({
27
+ children,
28
+ pageHeaderId = PAGE_HEADER_ID,
29
+ }: AppHeaderPortalProps) {
26
30
  const [container, setContainer] = useState<HTMLElement | null>(null);
27
31
 
28
32
  useLayoutEffect(() => {
29
- setContainer(document.getElementById(PAGE_HEADER_ID));
30
- }, []);
33
+ setContainer(document.getElementById(pageHeaderId));
34
+ }, [pageHeaderId]);
31
35
 
32
36
  if (!container) {
33
37
  return null;
@@ -3,6 +3,7 @@
3
3
  display inline-block
4
4
  width 100%
5
5
  height 100%
6
+ background-color var(--muted-50)
6
7
 
7
8
  .image
8
9
  display block
@@ -0,0 +1,125 @@
1
+ .loadingButton
2
+ gap 0.5rem
3
+
4
+ .avatarSkeleton
5
+ height 2rem
6
+ width 2rem
7
+ border-radius 9999px
8
+ background-color var(--color-muted)
9
+ animation pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite
10
+
11
+ .textSkeleton
12
+ height 1rem
13
+ width 5rem
14
+ border-radius 0.25rem
15
+ background-color var(--color-muted)
16
+ animation pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite
17
+
18
+ .loginButton
19
+ gap 0.5rem
20
+
21
+ .iconLg
22
+ height 1.25rem
23
+ width 1.25rem
24
+
25
+ .iconSm
26
+ height 0.75rem
27
+ width 0.75rem
28
+
29
+ .menuIcon
30
+ margin-right 0.5rem
31
+ height 1rem
32
+ width 1rem
33
+
34
+ .dropdownContent
35
+ width 14rem
36
+
37
+ .userButton
38
+ gap 0.5rem
39
+ height 52px
40
+ padding var(--p-2)
41
+
42
+ &.compact
43
+ padding 0
44
+ background-color transparent !important
45
+ transition transform 0.2s ease-in-out
46
+
47
+ &:hover
48
+ transform scale(1.1)
49
+
50
+ .avatar
51
+ height 2rem
52
+ width 2rem
53
+
54
+ .avatarImage
55
+ width 100%
56
+ height 100%
57
+ object-fit cover
58
+ border-radius inherit
59
+
60
+ .avatarFallback
61
+ background-color var(--color-primary)
62
+ color var(--color-primary-foreground)
63
+ background url('./avatar.svg') no-repeat center center
64
+ width 100%
65
+ height 100%
66
+ border-radius inherit
67
+
68
+ .userInfo
69
+ display flex
70
+ flex-direction column
71
+ align-items flex-start
72
+ text-align left
73
+ gap 0.25rem
74
+
75
+ .userName
76
+ font-size var(--text-sm)
77
+ font-weight 400
78
+ line-height 1
79
+ text-overflow ellipsis
80
+ overflow hidden
81
+ white-space nowrap
82
+ max-width 7.5rem
83
+
84
+ .userEmail
85
+ font-size var(--text-xs)
86
+ color var(--sb-slate-500)
87
+ line-height 1
88
+
89
+ .userLabel
90
+ padding 0
91
+ font-weight normal
92
+
93
+ .userLabelContent
94
+ display flex
95
+ align-items center
96
+ gap 0.5rem
97
+ padding 0.5rem
98
+ text-align left
99
+ font-size 0.875rem
100
+
101
+ .userDetails
102
+ display grid
103
+ flex 1
104
+ text-align left
105
+ font-size 0.875rem
106
+ line-height 1.25
107
+
108
+ .userDetailName
109
+ text-overflow ellipsis
110
+ overflow hidden
111
+ white-space nowrap
112
+ font-weight 500
113
+
114
+ .userDetailEmail
115
+ color var(--color-muted-foreground)
116
+ text-overflow ellipsis
117
+ overflow hidden
118
+ white-space nowrap
119
+ font-size 0.75rem
120
+
121
+ @keyframes pulse
122
+ 0%, 100%
123
+ opacity 1
124
+ 50%
125
+ opacity 0.5
@@ -0,0 +1,28 @@
1
+ // This file is automatically generated.
2
+ // Please do not change this file!
3
+ interface CssExports {
4
+ 'avatar': string;
5
+ 'avatarFallback': string;
6
+ 'avatarImage': string;
7
+ 'avatarSkeleton': string;
8
+ 'compact': string;
9
+ 'dropdownContent': string;
10
+ 'iconLg': string;
11
+ 'iconSm': string;
12
+ 'loadingButton': string;
13
+ 'loginButton': string;
14
+ 'menuIcon': string;
15
+ 'pulse': string;
16
+ 'textSkeleton': string;
17
+ 'userButton': string;
18
+ 'userDetailEmail': string;
19
+ 'userDetailName': string;
20
+ 'userDetails': string;
21
+ 'userEmail': string;
22
+ 'userInfo': string;
23
+ 'userLabel': string;
24
+ 'userLabelContent': string;
25
+ 'userName': string;
26
+ }
27
+ export const cssExports: CssExports;
28
+ export default cssExports;