@sybilion/uilib 1.2.6 → 1.2.8
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/assets/standalone-global.css +12 -11
- package/dist/esm/components/ui/Logo/Logo.js +7 -2
- package/dist/esm/components/ui/Logo/Logo.styl.js +1 -1
- package/dist/esm/components/ui/NavUserHeader/NavUserHeader.js +4 -2
- package/dist/esm/docs/contexts/theme-context.js +14 -0
- package/dist/esm/docs/lib/theme.js +23 -0
- package/dist/esm/types/src/components/ui/Logo/Logo.d.ts +5 -0
- package/dist/esm/types/src/components/ui/NavUserHeader/NavUserHeader.d.ts +1 -1
- package/dist/esm/types/src/components/ui/NavUserHeader/NavUserHeader.types.d.ts +0 -4
- package/dist/esm/types/src/docs/contexts/theme-context.d.ts +1 -0
- package/dist/esm/types/src/docs/pages/StandaloneAppLayoutPage/StandaloneAppLayoutPage.constants.d.ts +7 -0
- package/docs/standalone-apps.md +29 -11
- package/package.json +3 -2
- package/src/assets/logo.svg +3 -0
- package/src/components/ui/Logo/Logo.styl +2 -0
- package/src/components/ui/Logo/Logo.tsx +12 -1
- package/src/components/ui/NavUserHeader/NavUserHeader.tsx +15 -17
- package/src/components/ui/NavUserHeader/NavUserHeader.types.ts +0 -4
- package/src/docs/config/webpack.config.js +4 -3
- package/src/docs/contexts/theme-context.tsx +14 -1
- package/src/docs/pages/NavUserHeaderPage.tsx +1 -20
- package/src/docs/pages/StandaloneAppLayoutPage/StandaloneAppLayoutPage.constants.ts +444 -0
- package/src/docs/pages/StandaloneAppLayoutPage/StandaloneAppLayoutPage.tsx +9 -456
|
@@ -125,17 +125,18 @@
|
|
|
125
125
|
--font-family-heading: 'KMR Apparat', sans-serif;
|
|
126
126
|
--font-family-body: 'Manrope', sans-serif;
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
--brand-color
|
|
130
|
-
--brand-color-
|
|
131
|
-
--brand-color-
|
|
132
|
-
--brand-color-
|
|
133
|
-
--brand-color-
|
|
134
|
-
--brand-color-
|
|
135
|
-
--brand-color-
|
|
136
|
-
--brand-color-
|
|
137
|
-
--brand-color-
|
|
138
|
-
--brand-color-
|
|
128
|
+
/* Same mapping as sybilion-client/assets/globals.css (app variables) */
|
|
129
|
+
--brand-color: var(--sb-cyan-400);
|
|
130
|
+
--brand-color-50: var(--sb-cyan-50);
|
|
131
|
+
--brand-color-100: var(--sb-cyan-100);
|
|
132
|
+
--brand-color-200: var(--sb-cyan-200);
|
|
133
|
+
--brand-color-300: var(--sb-cyan-300);
|
|
134
|
+
--brand-color-400: var(--sb-cyan-400);
|
|
135
|
+
--brand-color-500: var(--sb-cyan-500);
|
|
136
|
+
--brand-color-600: var(--sb-cyan-600);
|
|
137
|
+
--brand-color-700: var(--sb-cyan-700);
|
|
138
|
+
--brand-color-800: var(--sb-cyan-800);
|
|
139
|
+
--brand-color-900: var(--sb-cyan-900);
|
|
139
140
|
--header-height: 94px;
|
|
140
141
|
--page-x-padding: var(--p-16);
|
|
141
142
|
--page-y-padding: var(--p-12);
|
|
@@ -2,8 +2,13 @@ import { jsxs, jsx } from 'react/jsx-runtime';
|
|
|
2
2
|
import cn from 'classnames';
|
|
3
3
|
import S from './Logo.styl.js';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Public URL for the Sybilion logo in standalone apps. Copy `@sybilion/uilib/logo.svg`
|
|
7
|
+
* to `public/logo.svg` (see standalone-apps guide). Same path as the favicon `<link href>`.
|
|
8
|
+
*/
|
|
9
|
+
const SYBILION_STANDALONE_LOGO_PUBLIC_URL = '/logo.svg';
|
|
5
10
|
function Logo({ showText = true, size = 'md', className, ...props }) {
|
|
6
|
-
return (jsxs("div", { className: cn(S.root, S[size], className), ...props, children: [jsx("img", { src:
|
|
11
|
+
return (jsxs("div", { className: cn(S.root, S[size], className), ...props, children: [jsx("img", { src: SYBILION_STANDALONE_LOGO_PUBLIC_URL, alt: showText ? '' : 'Sybilion', className: S.icon, ...(showText ? { 'aria-hidden': true } : {}) }), showText && jsx("span", { className: S.text, children: "Sybilion" })] }));
|
|
7
12
|
}
|
|
8
13
|
|
|
9
|
-
export { Logo };
|
|
14
|
+
export { Logo, SYBILION_STANDALONE_LOGO_PUBLIC_URL };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import styleInject from 'style-inject';
|
|
2
2
|
|
|
3
|
-
var css_248z = ".Logo_root__-IFVw{align-items:center;color:var(--color-foreground);display:flex;gap:8px}.Logo_icon__cE1BI{height:24px;width:auto}.Logo_text__YpU3U{font-family:var(--font-family-heading);line-height:48px}.Logo_sm__p25D- .Logo_icon__cE1BI{height:18px}.Logo_sm__p25D- .Logo_text__YpU3U{font-size:var(--text-lg);line-height:36px}.Logo_md__1Y9mG .Logo_icon__cE1BI{height:24px}.Logo_md__1Y9mG .Logo_text__YpU3U{font-size:var(--text-2xl);line-height:48px}.Logo_lg__V9Yy2 .Logo_icon__cE1BI{height:32px}.Logo_lg__V9Yy2 .Logo_text__YpU3U{font-size:var(--text-3xl);line-height:56px}";
|
|
3
|
+
var css_248z = ".Logo_root__-IFVw{align-items:center;color:var(--color-foreground);display:flex;gap:8px}.Logo_icon__cE1BI{display:block;flex-shrink:0;height:24px;width:auto}.Logo_text__YpU3U{font-family:var(--font-family-heading);line-height:48px}.Logo_sm__p25D- .Logo_icon__cE1BI{height:18px}.Logo_sm__p25D- .Logo_text__YpU3U{font-size:var(--text-lg);line-height:36px}.Logo_md__1Y9mG .Logo_icon__cE1BI{height:24px}.Logo_md__1Y9mG .Logo_text__YpU3U{font-size:var(--text-2xl);line-height:48px}.Logo_lg__V9Yy2 .Logo_icon__cE1BI{height:32px}.Logo_lg__V9Yy2 .Logo_text__YpU3U{font-size:var(--text-3xl);line-height:56px}";
|
|
4
4
|
var S = {"root":"Logo_root__-IFVw","icon":"Logo_icon__cE1BI","text":"Logo_text__YpU3U","sm":"Logo_sm__p25D-","md":"Logo_md__1Y9mG","lg":"Logo_lg__V9Yy2"};
|
|
5
5
|
styleInject(css_248z);
|
|
6
6
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import cn from 'classnames';
|
|
3
|
+
import { useTheme } from '../../../docs/contexts/theme-context.js';
|
|
3
4
|
import { UserCircleIcon, SunIcon, MoonIcon, SignOutIcon } from '@phosphor-icons/react';
|
|
4
5
|
import { ChevronDownIcon } from 'lucide-react';
|
|
5
6
|
import { Avatar } from '../Avatar/Avatar.js';
|
|
@@ -8,7 +9,8 @@ import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLab
|
|
|
8
9
|
import { Image } from '../Image/Image.js';
|
|
9
10
|
import S from './NavUserHeader.styl.js';
|
|
10
11
|
|
|
11
|
-
function NavUserHeader({ variant = 'default', isLoading = false, isAuthenticated, user = null, menuItems,
|
|
12
|
+
function NavUserHeader({ variant = 'default', isLoading = false, isAuthenticated, user = null, menuItems, onLogout, signInSlot, onSignInClick, }) {
|
|
13
|
+
const { toggleTheme, theme } = useTheme();
|
|
12
14
|
const authenticated = isAuthenticated ?? true;
|
|
13
15
|
const avatarUrl = user?.avatar ?? '';
|
|
14
16
|
const userName = user?.name ?? '';
|
|
@@ -22,7 +24,7 @@ function NavUserHeader({ variant = 'default', isLoading = false, isAuthenticated
|
|
|
22
24
|
}
|
|
23
25
|
return (jsxs(Button, { variant: "ghost", size: "sm", className: S.loginButton, type: "button", onClick: onSignInClick, children: [jsx(UserCircleIcon, { className: S.iconLg }), jsx("span", { children: "Log in" })] }));
|
|
24
26
|
}
|
|
25
|
-
return (jsxs(DropdownMenu, { children: [jsx(DropdownMenuTrigger, { asChild: true, children: jsxs(Button, { variant: "ghost", size: "sm", className: cn(S.userButton, variant === 'compact' && S.compact), children: [jsx(Avatar, { className: S.avatar, children: jsx(Image, { url: avatarUrl, alt: userName, fallback: jsx("div", { className: S.avatarFallback }) }) }), variant === 'default' && (jsxs(Fragment, { children: [jsxs("div", { className: S.userInfo, children: [jsx("span", { className: `${S.userName} ph-no-capture`, children: userName }), jsx("span", { className: S.userEmail, children: userEmail })] }), jsx(ChevronDownIcon, { className: S.iconSm })] }))] }) }), jsxs(DropdownMenuContent, { className: S.dropdownContent, align: "end", elevation: "md", children: [jsx(DropdownMenuLabel, { className: S.userLabel, children: jsxs("div", { className: S.userLabelContent, children: [jsx(Avatar, { className: S.avatar, children: jsx(Image, { url: avatarUrl, alt: userName, fallback: jsx("div", { className: S.avatarFallback }) }) }), jsxs("div", { className: S.userDetails, children: [jsx("span", { className: `${S.userDetailName} ph-no-capture`, children: userName }), jsx("span", { className: S.userDetailEmail, children: userEmail })] })] }) }), jsx(DropdownMenuSeparator, {}), jsxs(DropdownMenuGroup, { children: [menuItems,
|
|
27
|
+
return (jsxs(DropdownMenu, { children: [jsx(DropdownMenuTrigger, { asChild: true, children: jsxs(Button, { variant: "ghost", size: "sm", className: cn(S.userButton, variant === 'compact' && S.compact), children: [jsx(Avatar, { className: S.avatar, children: jsx(Image, { url: avatarUrl, alt: userName, fallback: jsx("div", { className: S.avatarFallback }) }) }), variant === 'default' && (jsxs(Fragment, { children: [jsxs("div", { className: S.userInfo, children: [jsx("span", { className: `${S.userName} ph-no-capture`, children: userName }), jsx("span", { className: S.userEmail, children: userEmail })] }), jsx(ChevronDownIcon, { className: S.iconSm })] }))] }) }), jsxs(DropdownMenuContent, { className: S.dropdownContent, align: "end", elevation: "md", children: [jsx(DropdownMenuLabel, { className: S.userLabel, children: jsxs("div", { className: S.userLabelContent, children: [jsx(Avatar, { className: S.avatar, children: jsx(Image, { url: avatarUrl, alt: userName, fallback: jsx("div", { className: S.avatarFallback }) }) }), jsxs("div", { className: S.userDetails, children: [jsx("span", { className: `${S.userDetailName} ph-no-capture`, children: userName }), jsx("span", { className: S.userDetailEmail, children: userEmail })] })] }) }), jsx(DropdownMenuSeparator, {}), jsxs(DropdownMenuGroup, { children: [menuItems, jsx(DropdownMenuItem, { onSelect: toggleTheme, children: theme === 'dark' ? (jsxs(Fragment, { children: [jsx(SunIcon, {}), "Light theme"] })) : (jsxs(Fragment, { children: [jsx(MoonIcon, {}), "Dark theme"] })) })] }), jsx(DropdownMenuSeparator, {}), jsxs(DropdownMenuItem, { variant: "destructive", onSelect: () => onLogout(), children: [jsx(SignOutIcon, {}), "Log out"] })] })] }));
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
export { NavUserHeader };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import 'react/jsx-runtime';
|
|
2
|
+
import { createContext, useContext } from 'react';
|
|
3
|
+
import '@homecode/ui';
|
|
4
|
+
import '../lib/theme.js';
|
|
5
|
+
|
|
6
|
+
const ThemeContext = createContext({
|
|
7
|
+
theme: 'light',
|
|
8
|
+
isDarkMode: false,
|
|
9
|
+
setTheme: () => { },
|
|
10
|
+
toggleTheme: () => { },
|
|
11
|
+
});
|
|
12
|
+
const useTheme = () => useContext(ThemeContext);
|
|
13
|
+
|
|
14
|
+
export { useTheme };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ThemeHelpers, ThemeDefaults } from '@homecode/ui';
|
|
2
|
+
|
|
3
|
+
const { colors, getColors, getConfig } = ThemeDefaults;
|
|
4
|
+
getColors();
|
|
5
|
+
getConfig();
|
|
6
|
+
({
|
|
7
|
+
light: {
|
|
8
|
+
...ThemeHelpers.colorsConfigToVars({
|
|
9
|
+
...getColors({
|
|
10
|
+
accent: colors.dark,
|
|
11
|
+
decent: colors.light,
|
|
12
|
+
}),
|
|
13
|
+
}),
|
|
14
|
+
},
|
|
15
|
+
dark: {
|
|
16
|
+
...ThemeHelpers.colorsConfigToVars({
|
|
17
|
+
...getColors({
|
|
18
|
+
accent: colors.light,
|
|
19
|
+
decent: colors.dark,
|
|
20
|
+
}),
|
|
21
|
+
}),
|
|
22
|
+
},
|
|
23
|
+
});
|
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
import type { LogoProps } from './Logo.types';
|
|
2
|
+
/**
|
|
3
|
+
* Public URL for the Sybilion logo in standalone apps. Copy `@sybilion/uilib/logo.svg`
|
|
4
|
+
* to `public/logo.svg` (see standalone-apps guide). Same path as the favicon `<link href>`.
|
|
5
|
+
*/
|
|
6
|
+
export declare const SYBILION_STANDALONE_LOGO_PUBLIC_URL: "/logo.svg";
|
|
2
7
|
export declare function Logo({ showText, size, className, ...props }: LogoProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { NavUserHeaderProps } from './NavUserHeader.types';
|
|
2
|
-
export declare function NavUserHeader({ variant, isLoading, isAuthenticated, user, menuItems,
|
|
2
|
+
export declare function NavUserHeader({ variant, isLoading, isAuthenticated, user, menuItems, onLogout, signInSlot, onSignInClick, }: NavUserHeaderProps): string | number | bigint | true | Iterable<import("react").ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<import("react").ReactNode>> | import("react/jsx-runtime").JSX.Element;
|
|
@@ -14,10 +14,6 @@ export type NavUserHeaderProps = {
|
|
|
14
14
|
user?: NavUserHeaderUser | null;
|
|
15
15
|
/** Rows inside the menu above theme toggle and logout. Use `DropdownMenuItem` nodes. */
|
|
16
16
|
menuItems?: ReactNode;
|
|
17
|
-
/** Current theme drives the toggle row label/icons. */
|
|
18
|
-
theme: 'light' | 'dark';
|
|
19
|
-
/** When set, renders the light/dark theme menu row. */
|
|
20
|
-
onThemeToggle?: () => void;
|
|
21
17
|
onLogout: () => void;
|
|
22
18
|
/** Replaces default “Log in” control when signed out. */
|
|
23
19
|
signInSlot?: ReactNode;
|
package/dist/esm/types/src/docs/pages/StandaloneAppLayoutPage/StandaloneAppLayoutPage.constants.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { WorkspaceAppEntry } from '#uilib/components/ui/WorkspaceAppSwitcher';
|
|
2
|
+
import { SidebarDatasetsItemsGroupedDataset } from '#uilib/components/widgets/SidebarDatasetsItemsGrouped';
|
|
3
|
+
export declare const MOCK_DATASETS: SidebarDatasetsItemsGroupedDataset[];
|
|
4
|
+
export type PreviewPanel = 'home' | 'datasets';
|
|
5
|
+
export declare const TEST_HEADER_ID = "test-header-id";
|
|
6
|
+
export declare const DOCS_WORKSPACE_APPS_LS_KEY = "uilib.docs.workspaceApps";
|
|
7
|
+
export declare const DOCS_PREVIEW_APPS: WorkspaceAppEntry[];
|
package/docs/standalone-apps.md
CHANGED
|
@@ -40,6 +40,23 @@ Import tokens/fonts once (typically `src/main.tsx`):
|
|
|
40
40
|
import '@sybilion/uilib/standalone-global.css';
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
+
### Branding (header logo + optional tab icon)
|
|
44
|
+
|
|
45
|
+
**Header:** **`SybilionAppHeader`** includes **`Logo`** from **`@sybilion/uilib`**. **`Logo`** uses **`SYBILION_STANDALONE_LOGO_PUBLIC_URL`** (**`'/logo.svg'`**). Vite serves that from **`public/logo.svg`**. Copy the published file once:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
mkdir -p public && cp node_modules/@sybilion/uilib/logo.svg public/logo.svg
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
That asset is **`src/assets/logo.svg`** in the package (**cyan** glyph). The **uilib docs** repo uses **`assets/logo.svg`** (**purple**) for its own site only—not what **`./logo.svg`** publishes.
|
|
52
|
+
|
|
53
|
+
**Browser tab (optional):** The favicon (tiny icon next to the tab title) and the document title come from **`index.html`**, not from React. **`Logo`** does not set them. To replace Vite’s default tab icon with the package logo, add **`href="/logo.svg"`** — same as **`SYBILION_STANDALONE_LOGO_PUBLIC_URL`** from **`@sybilion/uilib`** — plus **`<title>`** in **`index.html`** `<head>`:
|
|
54
|
+
|
|
55
|
+
```html
|
|
56
|
+
<link rel="icon" href="/logo.svg" type="image/svg+xml" />
|
|
57
|
+
<title>Your app name</title>
|
|
58
|
+
```
|
|
59
|
+
|
|
43
60
|
Mount the tree with `ReactDOM.createRoot` → `App` (wrap with `StrictMode` if you want).
|
|
44
61
|
|
|
45
62
|
### `package.json` scripts (required)
|
|
@@ -471,17 +488,18 @@ Composition: `PageScroll` → `AppShell` → `AppSidebar` → `AppShellMainConte
|
|
|
471
488
|
|
|
472
489
|
### Greenfield checklist (agents)
|
|
473
490
|
|
|
474
|
-
| Step | Deliverable
|
|
475
|
-
| ------------------ |
|
|
476
|
-
| React / router | **`react`**, **`react-dom`**, **`react-router-dom`**, **`@auth0/auth0-react`** (and **`vite`** if applicable) aligned with **`@sybilion/uilib`** **`package.json`** — §1 _React ecosystem versions_.
|
|
477
|
-
| Env files | **`.env.example`** (committed) + **`.env`** locally; **`PORT=3000`** recommended for Auth0 test tenant; **`VITE_*`** as in §1 table.
|
|
478
|
-
| Scripts | **`package.json`** includes mandatory **`dev`** (e.g. `"vite"`); optional **`build`**, **`preview`**.
|
|
479
|
-
| Vite proxy | **`vite.config.ts`** spreads **`sybilionStandaloneViteDev({ mode })`**; SDK **`baseUrl`** empty in dev (§2).
|
|
480
|
-
| Files | `src/libs/sybilion-sdk.ts`, `AppProviders.tsx`, `AppLayout.tsx`, `AppSidebar.tsx`, `App.tsx`, `main.tsx`, route pages under e.g. `src/pages/`.
|
|
481
|
-
|
|
|
482
|
-
|
|
|
483
|
-
|
|
|
484
|
-
|
|
|
491
|
+
| Step | Deliverable |
|
|
492
|
+
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
493
|
+
| React / router | **`react`**, **`react-dom`**, **`react-router-dom`**, **`@auth0/auth0-react`** (and **`vite`** if applicable) aligned with **`@sybilion/uilib`** **`package.json`** — §1 _React ecosystem versions_. |
|
|
494
|
+
| Env files | **`.env.example`** (committed) + **`.env`** locally; **`PORT=3000`** recommended for Auth0 test tenant; **`VITE_*`** as in §1 table. |
|
|
495
|
+
| Scripts | **`package.json`** includes mandatory **`dev`** (e.g. `"vite"`); optional **`build`**, **`preview`**. |
|
|
496
|
+
| Vite proxy | **`vite.config.ts`** spreads **`sybilionStandaloneViteDev({ mode })`**; SDK **`baseUrl`** empty in dev (§2). |
|
|
497
|
+
| Files | `src/libs/sybilion-sdk.ts`, `AppProviders.tsx`, `AppLayout.tsx`, `AppSidebar.tsx`, `App.tsx`, `main.tsx`, route pages under e.g. `src/pages/`. |
|
|
498
|
+
| Branding | **`public/logo.svg`** for **`Logo`** / **`SybilionAppHeader`**: `cp node_modules/@sybilion/uilib/logo.svg public/logo.svg`. **`index.html`**: `<title>`; optional **`<link rel="icon">`** for tab icon (§1 _Branding_). |
|
|
499
|
+
| Pages + UI | **§4 Route page body** (mandatory stack) + **Discovering** (barrel-first; bespoke markup only when no export fits). |
|
|
500
|
+
| Auth0 | SPA callback / logout URLs + allowed web origins → **`http://localhost:<PORT>`** for local dev and deploy URLs (and previews). |
|
|
501
|
+
| API | **Dev:** proxy handles API traffic (no browser CORS to API). **Prod:** Sybilion backend **CORS** → your deploy `Origin`, unless API is same-origin. |
|
|
502
|
+
| Go (if applicable) | Server proxies **`/api`** to Sybilion; SPA dev **`baseUrl`** stays `''` when same-origin. |
|
|
485
503
|
|
|
486
504
|
### Glossary (high-use pieces)
|
|
487
505
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sybilion/uilib",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.8",
|
|
4
4
|
"description": "Sybilion Design System — React UI components (Webpack + Stylus)",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
@@ -35,7 +35,8 @@
|
|
|
35
35
|
"types": "./dist/standalone/vite-sybilion-standalone-dev.d.ts",
|
|
36
36
|
"import": "./dist/standalone/vite-sybilion-standalone-dev.js",
|
|
37
37
|
"default": "./dist/standalone/vite-sybilion-standalone-dev.js"
|
|
38
|
-
}
|
|
38
|
+
},
|
|
39
|
+
"./logo.svg": "./src/assets/logo.svg"
|
|
39
40
|
},
|
|
40
41
|
"files": [
|
|
41
42
|
"assets",
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
|
2
|
+
<path fill="#59f0ff" d="M13.43 24.023h-2.836v-2.836h2.836zm-2.835-2.844H5.63v-2.74h4.965Zm10.527-2.74h-2.726v2.74h-4.965v-2.74h4.952v-4.965h2.74zm-15.5 0h-2.74v-4.965h2.74ZM2.87 13.462H.034v-2.836H2.87Zm21.096 0H21.13v-2.836h2.836zM5.617 10.635h-2.74V5.669h2.74zm15.505 0h-2.74V5.669h2.74zM10.588 2.927v2.74H5.623v-2.74Zm2.842 0h4.96v2.74h-4.966v-2.74h-2.83V.09h2.836z" style="stroke-width:.13252" />
|
|
3
|
+
</svg>
|
|
@@ -3,6 +3,12 @@ import cn from 'classnames';
|
|
|
3
3
|
import S from './Logo.styl';
|
|
4
4
|
import type { LogoProps } from './Logo.types';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Public URL for the Sybilion logo in standalone apps. Copy `@sybilion/uilib/logo.svg`
|
|
8
|
+
* to `public/logo.svg` (see standalone-apps guide). Same path as the favicon `<link href>`.
|
|
9
|
+
*/
|
|
10
|
+
export const SYBILION_STANDALONE_LOGO_PUBLIC_URL = '/logo.svg' as const;
|
|
11
|
+
|
|
6
12
|
export function Logo({
|
|
7
13
|
showText = true,
|
|
8
14
|
size = 'md',
|
|
@@ -11,7 +17,12 @@ export function Logo({
|
|
|
11
17
|
}: LogoProps) {
|
|
12
18
|
return (
|
|
13
19
|
<div className={cn(S.root, S[size], className)} {...props}>
|
|
14
|
-
<img
|
|
20
|
+
<img
|
|
21
|
+
src={SYBILION_STANDALONE_LOGO_PUBLIC_URL}
|
|
22
|
+
alt={showText ? '' : 'Sybilion'}
|
|
23
|
+
className={S.icon}
|
|
24
|
+
{...(showText ? { 'aria-hidden': true } : {})}
|
|
25
|
+
/>
|
|
15
26
|
{showText && <span className={S.text}>Sybilion</span>}
|
|
16
27
|
</div>
|
|
17
28
|
);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import cn from 'classnames';
|
|
2
2
|
|
|
3
|
+
import { useTheme } from '#uilib/docs/contexts/theme-context';
|
|
3
4
|
import {
|
|
4
5
|
MoonIcon,
|
|
5
6
|
SignOutIcon,
|
|
@@ -29,12 +30,11 @@ export function NavUserHeader({
|
|
|
29
30
|
isAuthenticated,
|
|
30
31
|
user = null,
|
|
31
32
|
menuItems,
|
|
32
|
-
theme,
|
|
33
|
-
onThemeToggle,
|
|
34
33
|
onLogout,
|
|
35
34
|
signInSlot,
|
|
36
35
|
onSignInClick,
|
|
37
36
|
}: NavUserHeaderProps) {
|
|
37
|
+
const { toggleTheme, theme } = useTheme();
|
|
38
38
|
const authenticated = isAuthenticated ?? true;
|
|
39
39
|
|
|
40
40
|
const avatarUrl = user?.avatar ?? '';
|
|
@@ -121,21 +121,19 @@ export function NavUserHeader({
|
|
|
121
121
|
<DropdownMenuSeparator />
|
|
122
122
|
<DropdownMenuGroup>
|
|
123
123
|
{menuItems}
|
|
124
|
-
{
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
</DropdownMenuItem>
|
|
138
|
-
) : null}
|
|
124
|
+
<DropdownMenuItem onSelect={toggleTheme}>
|
|
125
|
+
{theme === 'dark' ? (
|
|
126
|
+
<>
|
|
127
|
+
<SunIcon />
|
|
128
|
+
Light theme
|
|
129
|
+
</>
|
|
130
|
+
) : (
|
|
131
|
+
<>
|
|
132
|
+
<MoonIcon />
|
|
133
|
+
Dark theme
|
|
134
|
+
</>
|
|
135
|
+
)}
|
|
136
|
+
</DropdownMenuItem>
|
|
139
137
|
</DropdownMenuGroup>
|
|
140
138
|
<DropdownMenuSeparator />
|
|
141
139
|
<DropdownMenuItem variant="destructive" onSelect={() => onLogout()}>
|
|
@@ -16,10 +16,6 @@ export type NavUserHeaderProps = {
|
|
|
16
16
|
user?: NavUserHeaderUser | null;
|
|
17
17
|
/** Rows inside the menu above theme toggle and logout. Use `DropdownMenuItem` nodes. */
|
|
18
18
|
menuItems?: ReactNode;
|
|
19
|
-
/** Current theme drives the toggle row label/icons. */
|
|
20
|
-
theme: 'light' | 'dark';
|
|
21
|
-
/** When set, renders the light/dark theme menu row. */
|
|
22
|
-
onThemeToggle?: () => void;
|
|
23
19
|
onLogout: () => void;
|
|
24
20
|
/** Replaces default “Log in” control when signed out. */
|
|
25
21
|
signInSlot?: ReactNode;
|
|
@@ -20,6 +20,7 @@ const FaviconWebpackPlugin = require('favicons-webpack-plugin');
|
|
|
20
20
|
const pkg = require('../../../package.json');
|
|
21
21
|
|
|
22
22
|
const themeStyl = pathResolve(paths.src, 'theme.styl');
|
|
23
|
+
const logoSvgPath = pathResolve(paths.assets, 'logo.svg');
|
|
23
24
|
|
|
24
25
|
export default (env, argv) => {
|
|
25
26
|
const isDev = argv.mode === 'development';
|
|
@@ -148,7 +149,7 @@ export default (env, argv) => {
|
|
|
148
149
|
noErrorOnMissing: true,
|
|
149
150
|
},
|
|
150
151
|
{
|
|
151
|
-
from:
|
|
152
|
+
from: logoSvgPath,
|
|
152
153
|
to: paths.build,
|
|
153
154
|
noErrorOnMissing: true,
|
|
154
155
|
},
|
|
@@ -175,9 +176,9 @@ export default (env, argv) => {
|
|
|
175
176
|
minifyURLs: true,
|
|
176
177
|
},
|
|
177
178
|
}),
|
|
178
|
-
existsSync(
|
|
179
|
+
existsSync(logoSvgPath) &&
|
|
179
180
|
new FaviconWebpackPlugin({
|
|
180
|
-
logo:
|
|
181
|
+
logo: logoSvgPath,
|
|
181
182
|
mode: 'webapp',
|
|
182
183
|
devMode: 'webapp',
|
|
183
184
|
favicons: {
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
createContext,
|
|
3
|
+
useCallback,
|
|
4
|
+
useContext,
|
|
5
|
+
useEffect,
|
|
6
|
+
useState,
|
|
7
|
+
} from 'react';
|
|
2
8
|
|
|
3
9
|
import { Theme as ThemeRoot } from '@homecode/ui';
|
|
4
10
|
|
|
@@ -10,10 +16,12 @@ const ThemeContext = createContext<{
|
|
|
10
16
|
theme: ThemeMode;
|
|
11
17
|
isDarkMode: boolean;
|
|
12
18
|
setTheme: (theme: ThemeMode) => void;
|
|
19
|
+
toggleTheme: () => void;
|
|
13
20
|
}>({
|
|
14
21
|
theme: 'light',
|
|
15
22
|
isDarkMode: false,
|
|
16
23
|
setTheme: () => {},
|
|
24
|
+
toggleTheme: () => {},
|
|
17
25
|
});
|
|
18
26
|
|
|
19
27
|
export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
|
@@ -25,6 +33,10 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
|
|
25
33
|
getThemeConfig(theme === 'dark'),
|
|
26
34
|
);
|
|
27
35
|
|
|
36
|
+
const toggleTheme = useCallback(() => {
|
|
37
|
+
setTheme(theme === 'dark' ? 'light' : 'dark');
|
|
38
|
+
}, [theme, setTheme]);
|
|
39
|
+
|
|
28
40
|
useEffect(() => {
|
|
29
41
|
setCurrThemeConfig(getThemeConfig(theme === 'dark'));
|
|
30
42
|
}, [theme]);
|
|
@@ -44,6 +56,7 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
|
|
44
56
|
theme,
|
|
45
57
|
isDarkMode: theme === 'dark',
|
|
46
58
|
setTheme,
|
|
59
|
+
toggleTheme,
|
|
47
60
|
}}
|
|
48
61
|
>
|
|
49
62
|
<ThemeRoot config={currThemeConfig} />
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { useCallback, useEffect, useState } from 'react';
|
|
2
|
-
|
|
3
1
|
import { DropdownMenuItem } from '#uilib/components/ui/DropdownMenu';
|
|
4
2
|
import { NavUserHeader } from '#uilib/components/ui/NavUserHeader';
|
|
5
3
|
import { PageContentSection } from '#uilib/components/ui/Page';
|
|
@@ -9,19 +7,6 @@ import { AppPageHeader } from '../components/AppPageHeader/AppPageHeader';
|
|
|
9
7
|
import { DocsHeaderActions } from '../docsHeaderActions';
|
|
10
8
|
|
|
11
9
|
export default function NavUserHeaderPage() {
|
|
12
|
-
const [theme, setTheme] = useState<'light' | 'dark'>('light');
|
|
13
|
-
|
|
14
|
-
useEffect(() => {
|
|
15
|
-
document.documentElement.dataset.theme = theme;
|
|
16
|
-
return () => {
|
|
17
|
-
delete document.documentElement.dataset.theme;
|
|
18
|
-
};
|
|
19
|
-
}, [theme]);
|
|
20
|
-
|
|
21
|
-
const onThemeToggle = useCallback(() => {
|
|
22
|
-
setTheme(t => (t === 'dark' ? 'light' : 'dark'));
|
|
23
|
-
}, []);
|
|
24
|
-
|
|
25
10
|
const customMenuItems = (
|
|
26
11
|
<>
|
|
27
12
|
<DropdownMenuItem>
|
|
@@ -40,7 +25,7 @@ export default function NavUserHeaderPage() {
|
|
|
40
25
|
<AppPageHeader
|
|
41
26
|
breadcrumbs={[{ label: 'NavUserHeader' }]}
|
|
42
27
|
title="NavUserHeader"
|
|
43
|
-
subheader="User menu with label, custom rows, theme toggle, and logout."
|
|
28
|
+
subheader="User menu with label, custom rows, theme toggle (docs ThemeProvider), and logout."
|
|
44
29
|
actions={<DocsHeaderActions />}
|
|
45
30
|
/>
|
|
46
31
|
<PageContentSection
|
|
@@ -56,8 +41,6 @@ export default function NavUserHeaderPage() {
|
|
|
56
41
|
email: 'demo@sybilion.io',
|
|
57
42
|
avatar: '',
|
|
58
43
|
}}
|
|
59
|
-
theme={theme}
|
|
60
|
-
onThemeToggle={onThemeToggle}
|
|
61
44
|
onLogout={() => {
|
|
62
45
|
console.info('[docs] logout');
|
|
63
46
|
}}
|
|
@@ -75,8 +58,6 @@ export default function NavUserHeaderPage() {
|
|
|
75
58
|
email: 'compact@sybilion.io',
|
|
76
59
|
avatar: '',
|
|
77
60
|
}}
|
|
78
|
-
theme={theme}
|
|
79
|
-
onThemeToggle={onThemeToggle}
|
|
80
61
|
onLogout={() => {
|
|
81
62
|
console.info('[docs] logout compact');
|
|
82
63
|
}}
|