@sybilion/uilib 1.2.9 → 1.2.11

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 (69) hide show
  1. package/README.md +10 -7
  2. package/dist/esm/components/ui/AppHeader/AppHeader.styl.js +1 -1
  3. package/dist/esm/components/ui/Logo/Logo.js +2 -1
  4. package/dist/esm/components/ui/NavUserHeader/NavUserHeader.js +19 -6
  5. package/dist/esm/components/ui/NavUserHeader/NavUserHeader.styl.js +2 -2
  6. package/dist/esm/components/ui/Sidebar/Sidebar.styl.js +1 -1
  7. package/dist/esm/components/widgets/SidebarDatasetsItemsGrouped/SidebarDatasetsItemsGrouped.js +8 -2
  8. package/dist/esm/components/widgets/SignInPage/SignInPage.js +2 -2
  9. package/dist/esm/components/widgets/SybilionAppHeader/SybilionAppHeader.js +2 -2
  10. package/dist/esm/components/widgets/SybilionAuthLayout/SybilionAuthHeadline.styl.js +1 -1
  11. package/dist/esm/components/widgets/SybilionAuthLayout/SybilionAuthLayout.js +3 -8
  12. package/dist/esm/components/widgets/SybilionAuthLayout/SybilionAuthLayout.styl.js +2 -2
  13. package/dist/esm/components/widgets/SybilionSignInPanel/SybilionSignInPanel.styl.js +1 -1
  14. package/dist/esm/contexts/theme-context.js +44 -0
  15. package/dist/esm/docs/lib/theme.js +35 -3
  16. package/dist/esm/index.js +3 -1
  17. package/dist/esm/sybilion-auth/SybilionAuthProvider.js +23 -10
  18. package/dist/esm/types/src/components/ui/Logo/Logo.d.ts +2 -1
  19. package/dist/esm/types/src/components/ui/NavUserHeader/NavUserHeader.d.ts +1 -1
  20. package/dist/esm/types/src/components/ui/NavUserHeader/NavUserHeader.types.d.ts +3 -0
  21. package/dist/esm/types/src/components/widgets/SidebarDatasetsItemsGrouped/SidebarDatasetsItemsGrouped.d.ts +4 -1
  22. package/dist/esm/types/src/components/widgets/SignInPage/SignInPage.d.ts +2 -2
  23. package/dist/esm/types/src/components/widgets/SybilionAppHeader/SybilionAppHeader.d.ts +5 -1
  24. package/dist/esm/types/src/components/widgets/SybilionAuthLayout/SybilionAuthLayout.d.ts +2 -5
  25. package/dist/esm/types/src/components/widgets/SybilionAuthLayout/index.d.ts +1 -1
  26. package/dist/esm/types/src/contexts/theme-context.d.ts +20 -0
  27. package/dist/esm/types/src/docs/contexts/theme-context.d.ts +1 -10
  28. package/dist/esm/types/src/docs/lib/theme.d.ts +5 -1
  29. package/dist/esm/types/src/docs/pages/SybilionAuthLayoutPage.d.ts +1 -0
  30. package/dist/esm/types/src/index.d.ts +2 -0
  31. package/dist/esm/types/src/sybilion-auth/SybilionAuthProvider.d.ts +10 -1
  32. package/package.json +2 -4
  33. package/src/components/ui/AppHeader/AppHeader.styl +2 -0
  34. package/src/components/ui/Logo/Logo.tsx +2 -1
  35. package/src/components/ui/NavUserHeader/NavUserHeader.styl +2 -20
  36. package/src/components/ui/NavUserHeader/NavUserHeader.styl.d.ts +0 -3
  37. package/src/components/ui/NavUserHeader/NavUserHeader.tsx +40 -30
  38. package/src/components/ui/NavUserHeader/NavUserHeader.types.ts +3 -0
  39. package/src/components/ui/Sidebar/Sidebar.styl +2 -2
  40. package/src/components/widgets/SidebarDatasetsItemsGrouped/SidebarDatasetsItemsGrouped.tsx +14 -3
  41. package/src/components/widgets/SignInPage/SignInPage.tsx +1 -3
  42. package/src/components/widgets/SybilionAppHeader/SybilionAppHeader.tsx +8 -0
  43. package/src/components/widgets/SybilionAuthLayout/SybilionAuthHeadline.styl +2 -2
  44. package/src/components/widgets/SybilionAuthLayout/SybilionAuthHeadline.styl.d.ts +10 -2
  45. package/src/components/widgets/SybilionAuthLayout/SybilionAuthLayout.styl +20 -9
  46. package/src/components/widgets/SybilionAuthLayout/SybilionAuthLayout.styl.d.ts +16 -2
  47. package/src/components/widgets/SybilionAuthLayout/SybilionAuthLayout.tsx +4 -17
  48. package/src/components/widgets/SybilionAuthLayout/index.ts +0 -1
  49. package/src/components/widgets/SybilionSignInPanel/SybilionSignInPanel.styl +3 -0
  50. package/src/components/widgets/SybilionSignInPanel/SybilionSignInPanel.styl.d.ts +12 -2
  51. package/src/contexts/theme-context.tsx +106 -0
  52. package/src/docs/App/ThemeToggle.tsx +1 -1
  53. package/src/docs/DocsShell.tsx +16 -7
  54. package/src/docs/components/DocsSidebar/DocsSidebar.tsx +1 -0
  55. package/src/docs/contexts/theme-context.tsx +8 -68
  56. package/src/docs/index.tsx +1 -1
  57. package/src/docs/lib/theme.ts +13 -2
  58. package/src/docs/pages/ChartAreaInteractivePage.tsx +2 -2
  59. package/src/docs/pages/StandaloneAppLayoutPage/StandaloneAppLayoutPage.styl +1 -1
  60. package/src/docs/pages/StandaloneAppLayoutPage/StandaloneAppLayoutPage.tsx +5 -4
  61. package/src/docs/pages/SybilionAuthLayoutPage.tsx +47 -0
  62. package/src/docs/registry.ts +3 -3
  63. package/src/index.ts +2 -0
  64. package/src/sybilion-auth/SybilionAuthProvider.tsx +34 -8
  65. package/assets/standalone-global.css +0 -257
  66. package/dist/esm/docs/contexts/theme-context.js +0 -14
  67. package/dist/esm/types/src/docs/pages/SybilionAuthProviderPage.d.ts +0 -1
  68. package/docs/standalone-apps.md +0 -552
  69. package/src/docs/pages/SybilionAuthProviderPage.tsx +0 -40
@@ -1,10 +1,10 @@
1
1
  import type { RedirectLoginOptions } from '@auth0/auth0-react';
2
2
  import { type SybilionAuthLayoutProps } from '#uilib/components/widgets/SybilionAuthLayout';
3
3
  import { type SybilionSignInPanelProps } from '../SybilionSignInPanel';
4
- export type SignInPageProps = Pick<SybilionAuthLayoutProps, 'heroBackgroundUrl' | 'logoSize' | 'containerClassName'> & Pick<SybilionSignInPanelProps, 'forgotPasswordTo' | 'releasesTo' | 'versionLabel' | 'primaryButtonLabel' | 'connectingLabel'> & {
4
+ export type SignInPageProps = Pick<SybilionAuthLayoutProps, 'logoSize' | 'containerClassName'> & Pick<SybilionSignInPanelProps, 'forgotPasswordTo' | 'releasesTo' | 'versionLabel' | 'primaryButtonLabel' | 'connectingLabel'> & {
5
5
  title?: string;
6
6
  subtitle?: string;
7
7
  /** Extra Auth0 `loginWithRedirect` options; merged with default sign-in params. */
8
8
  loginRedirectOptions?: RedirectLoginOptions;
9
9
  };
10
- export declare function SignInPage({ title, subtitle, forgotPasswordTo, releasesTo, versionLabel, primaryButtonLabel, connectingLabel, loginRedirectOptions, heroBackgroundUrl, logoSize, containerClassName, }: SignInPageProps): import("react/jsx-runtime").JSX.Element;
10
+ export declare function SignInPage({ title, subtitle, forgotPasswordTo, releasesTo, versionLabel, primaryButtonLabel, connectingLabel, loginRedirectOptions, logoSize, containerClassName, }: SignInPageProps): import("react/jsx-runtime").JSX.Element;
@@ -5,10 +5,14 @@ export type SybilionAppHeaderProps = WorkspaceAppSwitcherProps & NavUserHeaderPr
5
5
  pageHeaderId?: string;
6
6
  actionsAnchorId?: string;
7
7
  actionsAnchorClassName?: string;
8
+ /** Renders before `NavUserHeader` inside the page actions anchor (e.g. impersonation). */
9
+ actionsStart?: ReactNode;
10
+ /** Renders after `NavUserHeader` inside the page actions anchor (e.g. notifications). */
11
+ actionsEnd?: ReactNode;
8
12
  /** Branded markup; omit for default lucide tile + «Sybilion». */
9
13
  logo?: ReactNode;
10
14
  logoAreaClassName?: string;
11
15
  /** Applies vertical offset when a welcome banner consumes top space (CSS `--welcome-banner-height` on shell). */
12
16
  welcomeBannerOffset?: boolean;
13
17
  };
14
- export declare function SybilionAppHeader({ pageHeaderId, actionsAnchorId, actionsAnchorClassName, pathname, onNavigate, authenticated, defaultApps, appsStorageKey, logo, logoAreaClassName, welcomeBannerOffset, ...navUserHeaderProps }: SybilionAppHeaderProps): import("react/jsx-runtime").JSX.Element;
18
+ export declare function SybilionAppHeader({ pageHeaderId, actionsAnchorId, actionsAnchorClassName, actionsStart, actionsEnd, pathname, onNavigate, authenticated, defaultApps, appsStorageKey, logo, logoAreaClassName, welcomeBannerOffset, ...navUserHeaderProps }: SybilionAppHeaderProps): import("react/jsx-runtime").JSX.Element;
@@ -1,14 +1,11 @@
1
1
  import type { ReactNode } from 'react';
2
2
  import type { LogoSize } from '#uilib/components/ui/Logo/Logo.types';
3
- /** Same convention as {@link SYBILION_STANDALONE_LOGO_PUBLIC_URL}: copy `sybilion-bg.svg` from the package into `public/`. */
4
- export declare const SYBILION_STANDALONE_AUTH_HERO_BG_PUBLIC_URL: "/sybilion_bg.svg";
5
3
  export type SybilionAuthLayoutProps = {
6
4
  title: string;
7
5
  subtitle?: string;
8
6
  children: ReactNode;
9
7
  logoSize?: LogoSize;
10
8
  containerClassName?: string;
11
- /** Public URL for the hero watermark SVG (default {@link SYBILION_STANDALONE_AUTH_HERO_BG_PUBLIC_URL}). */
12
- heroBackgroundUrl?: string;
9
+ style?: React.CSSProperties;
13
10
  };
14
- export declare function SybilionAuthLayout({ title, subtitle, children, logoSize, containerClassName, heroBackgroundUrl, }: SybilionAuthLayoutProps): import("react/jsx-runtime").JSX.Element;
11
+ export declare function SybilionAuthLayout({ title, subtitle, children, logoSize, containerClassName, style, }: SybilionAuthLayoutProps): import("react/jsx-runtime").JSX.Element;
@@ -1,2 +1,2 @@
1
- export { SybilionAuthLayout, SYBILION_STANDALONE_AUTH_HERO_BG_PUBLIC_URL, type SybilionAuthLayoutProps, } from './SybilionAuthLayout';
1
+ export { SybilionAuthLayout, type SybilionAuthLayoutProps, } from './SybilionAuthLayout';
2
2
  export { SybilionAuthHeadline } from './SybilionAuthHeadline';
@@ -0,0 +1,20 @@
1
+ import { getThemeConfig as defaultGetThemeConfig, type GetThemeConfigOptions } from '#uilib/docs/lib/theme';
2
+ export type ThemeMode = 'light' | 'dark';
3
+ export type GetThemeConfigFn = (isDarkTheme: boolean, options?: GetThemeConfigOptions) => ReturnType<typeof defaultGetThemeConfig>;
4
+ export type { GetThemeConfigOptions };
5
+ export type ThemeProviderProps = {
6
+ children: React.ReactNode;
7
+ /** When false, DOM classes still update but theme is not persisted to `localStorage`. */
8
+ allowLocalStorage?: boolean;
9
+ /** Override docs default active/accent token (`DEFAULT_THEME_ACTIVE_COLOR`). Passed into `getThemeConfig`. */
10
+ activeColor?: string;
11
+ /** Homecode theme config; defaults to uilib docs palette. */
12
+ getThemeConfig?: GetThemeConfigFn;
13
+ };
14
+ export declare function ThemeProvider({ children, allowLocalStorage, activeColor, getThemeConfig: getThemeConfigProp, }: ThemeProviderProps): import("react/jsx-runtime").JSX.Element;
15
+ export declare const useTheme: () => {
16
+ theme: ThemeMode;
17
+ isDarkMode: boolean;
18
+ setTheme: (theme: ThemeMode) => void;
19
+ toggleTheme: () => void;
20
+ };
@@ -1,10 +1 @@
1
- export type ThemeMode = 'light' | 'dark';
2
- export declare function ThemeProvider({ children }: {
3
- children: React.ReactNode;
4
- }): import("react/jsx-runtime").JSX.Element;
5
- export declare const useTheme: () => {
6
- theme: ThemeMode;
7
- isDarkMode: boolean;
8
- setTheme: (theme: ThemeMode) => void;
9
- toggleTheme: () => void;
10
- };
1
+ export { ThemeProvider, useTheme, type GetThemeConfigFn, type GetThemeConfigOptions, type ThemeMode, type ThemeProviderProps, } from '#uilib/contexts/theme-context';
@@ -1,5 +1,9 @@
1
+ export declare const DEFAULT_THEME_ACTIVE_COLOR = "#9c59ff";
2
+ export type GetThemeConfigOptions = {
3
+ activeColor?: string;
4
+ };
1
5
  export declare const colorsConfig: {
2
6
  light: {};
3
7
  dark: {};
4
8
  };
5
- export declare function getThemeConfig(isDarkTheme: boolean): any;
9
+ export declare function getThemeConfig(isDarkTheme: boolean, options?: GetThemeConfigOptions): any;
@@ -0,0 +1 @@
1
+ export default function SybilionAuthLayoutPage(): import("react/jsx-runtime").JSX.Element;
@@ -1,3 +1,5 @@
1
+ export * from './contexts/theme-context';
2
+ export { DEFAULT_THEME_ACTIVE_COLOR } from './docs/lib/theme';
1
3
  export * from './sybilion-auth';
2
4
  export * from './types/sybilionDatasetSnapshots';
3
5
  export * from './contexts/chat-context';
@@ -19,7 +19,16 @@ export type SybilionAuthProviderProps = {
19
19
  redirect_uri?: string;
20
20
  };
21
21
  sybilionTokenStorageKey?: string;
22
+ /**
23
+ * When set, passed to Auth0 `logout({ logoutParams: { returnTo } })` if
24
+ * {@link SybilionAuthProviderProps.useFederatedLogout} is true.
25
+ */
22
26
  logoutReturnTo?: string;
27
+ /**
28
+ * If true, redirect to Auth0 `/v2/logout` (clears IdP session). Default false matches
29
+ * sybilion-client: local session clear only, no redirect (`openUrl: false`).
30
+ */
31
+ useFederatedLogout?: boolean;
23
32
  };
24
33
  export type SybilionAuthContextValue = {
25
34
  apiBaseUrl: string;
@@ -39,4 +48,4 @@ export declare function sybilionApiFetch(apiBaseUrl: string, bearerToken: string
39
48
  export declare function createSybilionApiFetch(apiBaseUrl: string, getSybilionAccessToken: () => Promise<string | null>): (path: string, init?: RequestInit) => Promise<Response>;
40
49
  /** Authenticated fetch using {@link useSybilionAuth} context. */
41
50
  export declare function useSybilionApiFetch(): (path: string, init?: RequestInit) => Promise<Response>;
42
- export declare function SybilionAuthProvider({ children, sdk, auth0Domain, auth0ClientId, redirectUri, authorizationParams, sybilionTokenStorageKey, logoutReturnTo, }: SybilionAuthProviderProps): JSX.Element;
51
+ export declare function SybilionAuthProvider({ children, sdk, auth0Domain, auth0ClientId, redirectUri, authorizationParams, sybilionTokenStorageKey, logoutReturnTo, useFederatedLogout, }: SybilionAuthProviderProps): JSX.Element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sybilion/uilib",
3
- "version": "1.2.9",
3
+ "version": "1.2.11",
4
4
  "description": "Sybilion Design System — React UI components (Webpack + Stylus)",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -30,14 +30,12 @@
30
30
  "default": "./src/index.ts"
31
31
  },
32
32
  "./src/*": "./src/*",
33
- "./standalone-global.css": "./assets/standalone-global.css",
34
33
  "./vite-standalone-dev": {
35
34
  "types": "./dist/standalone/vite-sybilion-standalone-dev.d.ts",
36
35
  "import": "./dist/standalone/vite-sybilion-standalone-dev.js",
37
36
  "default": "./dist/standalone/vite-sybilion-standalone-dev.js"
38
37
  },
39
- "./logo.svg": "./src/assets/logo.svg",
40
- "./sybilion-bg.svg": "./src/assets/sybilion_bg.svg"
38
+ "./logo.svg": "./src/assets/logo.svg"
41
39
  },
42
40
  "files": [
43
41
  "assets",
@@ -7,6 +7,8 @@
7
7
  width 100%
8
8
  max-width calc(100vw - var(--sidebar-width) - var(--p-3))
9
9
  min-height var(--header-height)
10
+ padding-right var(--p-2)
11
+
10
12
  background-color var(--color-background)
11
13
 
12
14
  @media (min-width MOBILE)
@@ -5,7 +5,8 @@ import type { LogoProps } from './Logo.types';
5
5
 
6
6
  /**
7
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>`.
8
+ * to `public/logo.svg` (Sybilion app template README documents postinstall copies).
9
+ * Same path as the favicon `<link href>`.
9
10
  */
10
11
  export const SYBILION_STANDALONE_LOGO_PUBLIC_URL = '/logo.svg' as const;
11
12
 
@@ -67,10 +67,12 @@
67
67
 
68
68
  .userInfo
69
69
  display flex
70
+ flex 1
70
71
  flex-direction column
71
72
  align-items flex-start
72
73
  text-align left
73
74
  gap 0.25rem
75
+ min-width 0
74
76
 
75
77
  .userName
76
78
  font-size var(--text-sm)
@@ -98,26 +100,6 @@
98
100
  text-align left
99
101
  font-size 0.875rem
100
102
 
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
103
  @keyframes pulse
122
104
  0%, 100%
123
105
  opacity 1
@@ -15,9 +15,6 @@ interface CssExports {
15
15
  'pulse': string;
16
16
  'textSkeleton': string;
17
17
  'userButton': string;
18
- 'userDetailEmail': string;
19
- 'userDetailName': string;
20
- 'userDetails': string;
21
18
  'userEmail': string;
22
19
  'userInfo': string;
23
20
  'userLabel': string;
@@ -1,6 +1,7 @@
1
1
  import cn from 'classnames';
2
+ import { useMemo } from 'react';
2
3
 
3
- import { useTheme } from '#uilib/docs/contexts/theme-context';
4
+ import { useTheme } from '#uilib/contexts/theme-context';
4
5
  import {
5
6
  MoonIcon,
6
7
  SignOutIcon,
@@ -9,7 +10,7 @@ import {
9
10
  } from '@phosphor-icons/react';
10
11
  import { ChevronDownIcon } from 'lucide-react';
11
12
 
12
- import { Avatar } from '../Avatar';
13
+ import { Avatar, AvatarFallback, AvatarImage } from '../Avatar';
13
14
  import { Button } from '../Button';
14
15
  import {
15
16
  DropdownMenu,
@@ -20,7 +21,6 @@ import {
20
21
  DropdownMenuSeparator,
21
22
  DropdownMenuTrigger,
22
23
  } from '../DropdownMenu';
23
- import { Image } from '../Image';
24
24
  import S from './NavUserHeader.styl';
25
25
  import type { NavUserHeaderProps } from './NavUserHeader.types';
26
26
 
@@ -33,14 +33,46 @@ export function NavUserHeader({
33
33
  onLogout,
34
34
  signInSlot,
35
35
  onSignInClick,
36
+ theme: themeFromHost,
37
+ onThemeToggle: onThemeToggleFromHost,
36
38
  }: NavUserHeaderProps) {
37
- const { toggleTheme, theme } = useTheme();
39
+ const docsTheme = useTheme();
40
+ const hostControlsTheme =
41
+ themeFromHost !== undefined && onThemeToggleFromHost !== undefined;
42
+ const theme = hostControlsTheme ? themeFromHost : docsTheme.theme;
43
+ const toggleTheme = hostControlsTheme
44
+ ? onThemeToggleFromHost
45
+ : docsTheme.toggleTheme;
38
46
  const authenticated = isAuthenticated ?? true;
39
47
 
40
48
  const avatarUrl = user?.avatar ?? '';
41
49
  const userName = user?.name ?? '';
42
50
  const userEmail = user?.email ?? '';
43
51
 
52
+ const avatar = useMemo(() => {
53
+ const initials = userName
54
+ .split(' ')
55
+ .map(name => name.charAt(0))
56
+ .join('');
57
+
58
+ return (
59
+ <Avatar className={S.avatar}>
60
+ <AvatarImage src={avatarUrl || undefined} alt={userName} />
61
+ <AvatarFallback>{initials}</AvatarFallback>
62
+ </Avatar>
63
+ );
64
+ }, [avatarUrl, userName]);
65
+
66
+ const userIdentity = useMemo(
67
+ () => (
68
+ <div className={S.userInfo}>
69
+ <span className={`${S.userName} ph-no-capture`}>{userName}</span>
70
+ <span className={S.userEmail}>{userEmail}</span>
71
+ </div>
72
+ ),
73
+ [userEmail, userName],
74
+ );
75
+
44
76
  if (isLoading) {
45
77
  return (
46
78
  <Button variant="ghost" size="sm" disabled className={S.loadingButton}>
@@ -76,21 +108,10 @@ export function NavUserHeader({
76
108
  size="sm"
77
109
  className={cn(S.userButton, variant === 'compact' && S.compact)}
78
110
  >
79
- <Avatar className={S.avatar}>
80
- <Image
81
- url={avatarUrl}
82
- alt={userName}
83
- fallback={<div className={S.avatarFallback} />}
84
- />
85
- </Avatar>
111
+ {avatar}
86
112
  {variant === 'default' && (
87
113
  <>
88
- <div className={S.userInfo}>
89
- <span className={`${S.userName} ph-no-capture`}>
90
- {userName}
91
- </span>
92
- <span className={S.userEmail}>{userEmail}</span>
93
- </div>
114
+ {userIdentity}
94
115
  <ChevronDownIcon className={S.iconSm} />
95
116
  </>
96
117
  )}
@@ -103,19 +124,8 @@ export function NavUserHeader({
103
124
  >
104
125
  <DropdownMenuLabel className={S.userLabel}>
105
126
  <div className={S.userLabelContent}>
106
- <Avatar className={S.avatar}>
107
- <Image
108
- url={avatarUrl}
109
- alt={userName}
110
- fallback={<div className={S.avatarFallback} />}
111
- />
112
- </Avatar>
113
- <div className={S.userDetails}>
114
- <span className={`${S.userDetailName} ph-no-capture`}>
115
- {userName}
116
- </span>
117
- <span className={S.userDetailEmail}>{userEmail}</span>
118
- </div>
127
+ {avatar}
128
+ {userIdentity}
119
129
  </div>
120
130
  </DropdownMenuLabel>
121
131
  <DropdownMenuSeparator />
@@ -20,4 +20,7 @@ export type NavUserHeaderProps = {
20
20
  /** Replaces default “Log in” control when signed out. */
21
21
  signInSlot?: ReactNode;
22
22
  onSignInClick?: () => void;
23
+ /** When both are set, theme row uses these instead of uilib `ThemeProvider` context. */
24
+ theme?: 'light' | 'dark';
25
+ onThemeToggle?: () => void;
23
26
  };
@@ -193,7 +193,6 @@
193
193
  .sidebarContent
194
194
  position absolute
195
195
  display flex
196
- min-height 0
197
196
  flex 1
198
197
  flex-direction column
199
198
  gap var(--p-16)
@@ -201,7 +200,8 @@
201
200
 
202
201
  width 100%
203
202
  height 100vh
204
- max-height @height
203
+ min-height 0
204
+ max-height 100%
205
205
 
206
206
  @media (min-width MOBILE)
207
207
  height calc(100vh - var(--gap-top))
@@ -26,7 +26,10 @@ export type SidebarDatasetsItemsGroupedProps = {
26
26
  postItems?: React.ReactNode;
27
27
  selectedDatasetId?: number;
28
28
  onDatasetClick?: (datasetId: number) => void;
29
- /** When omitted, all groups start expanded. */
29
+ /**
30
+ * When set, expanded state resets to this list whenever `grouped` changes.
31
+ * Omit: start collapsed; any group containing `selectedDatasetId` opens (toggle still adds more).
32
+ */
30
33
  defaultExpandedGroupNames?: string[];
31
34
  className?: string;
32
35
  };
@@ -46,15 +49,23 @@ export function SidebarDatasetsItemsGrouped({
46
49
  [datasets, groupBy],
47
50
  );
48
51
 
52
+ /** Content key — inline `[]` must not retrigger reset every render (new array ref). */
53
+ const defaultExpandedKey =
54
+ defaultExpandedGroupNames === undefined
55
+ ? '__smart__'
56
+ : defaultExpandedGroupNames.join('\0');
57
+
49
58
  const [expanded, setExpanded] = useState<Set<string>>(new Set());
50
59
 
51
60
  useEffect(() => {
52
61
  if (defaultExpandedGroupNames !== undefined) {
53
62
  setExpanded(new Set(defaultExpandedGroupNames));
54
63
  } else {
55
- setExpanded(new Set(grouped.map(([name]) => name)));
64
+ setExpanded(new Set());
56
65
  }
57
- }, [grouped, defaultExpandedGroupNames]);
66
+ // Intentionally `defaultExpandedKey` not `defaultExpandedGroupNames` (reference stability).
67
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- key encodes array contents
68
+ }, [grouped, defaultExpandedKey]);
58
69
 
59
70
  useEffect(() => {
60
71
  if (selectedDatasetId == null) return;
@@ -13,7 +13,7 @@ import {
13
13
 
14
14
  export type SignInPageProps = Pick<
15
15
  SybilionAuthLayoutProps,
16
- 'heroBackgroundUrl' | 'logoSize' | 'containerClassName'
16
+ 'logoSize' | 'containerClassName'
17
17
  > &
18
18
  Pick<
19
19
  SybilionSignInPanelProps,
@@ -42,7 +42,6 @@ export function SignInPage({
42
42
  primaryButtonLabel,
43
43
  connectingLabel,
44
44
  loginRedirectOptions,
45
- heroBackgroundUrl,
46
45
  logoSize,
47
46
  containerClassName,
48
47
  }: SignInPageProps) {
@@ -65,7 +64,6 @@ export function SignInPage({
65
64
  <SybilionAuthLayout
66
65
  title={title}
67
66
  subtitle={subtitle}
68
- heroBackgroundUrl={heroBackgroundUrl}
69
67
  logoSize={logoSize}
70
68
  containerClassName={containerClassName}
71
69
  >
@@ -20,6 +20,10 @@ export type SybilionAppHeaderProps = WorkspaceAppSwitcherProps &
20
20
  pageHeaderId?: string;
21
21
  actionsAnchorId?: string;
22
22
  actionsAnchorClassName?: string;
23
+ /** Renders before `NavUserHeader` inside the page actions anchor (e.g. impersonation). */
24
+ actionsStart?: ReactNode;
25
+ /** Renders after `NavUserHeader` inside the page actions anchor (e.g. notifications). */
26
+ actionsEnd?: ReactNode;
23
27
  /** Branded markup; omit for default lucide tile + «Sybilion». */
24
28
  logo?: ReactNode;
25
29
  logoAreaClassName?: string;
@@ -31,6 +35,8 @@ export function SybilionAppHeader({
31
35
  pageHeaderId,
32
36
  actionsAnchorId = PAGE_HEADER_ACTIONS_ID,
33
37
  actionsAnchorClassName,
38
+ actionsStart,
39
+ actionsEnd,
34
40
  pathname,
35
41
  onNavigate,
36
42
  authenticated,
@@ -67,7 +73,9 @@ export function SybilionAppHeader({
67
73
  id={actionsAnchorId}
68
74
  className={cn(S.actionsAnchor, actionsAnchorClassName)}
69
75
  >
76
+ {actionsStart}
70
77
  <NavUserHeader {...navUserHeaderProps} />
78
+ {actionsEnd}
71
79
  </div>
72
80
  </AppHeaderPortal>
73
81
  );
@@ -10,7 +10,7 @@
10
10
  height 100%
11
11
  font-family var(--font-family-heading)
12
12
  font-weight 300
13
- text-shadow 0 0 2px var(--background)
13
+ text-shadow 0 0 7px var(--background)
14
14
 
15
15
  .headline
16
16
  font-size 40px
@@ -23,4 +23,4 @@
23
23
  margin 0
24
24
 
25
25
  .headlineCyan
26
- color #27d1ef
26
+ color var(--brand-color)
@@ -1,2 +1,10 @@
1
- const mod: { [cls: string]: string };
2
- export default mod;
1
+ // This file is automatically generated.
2
+ // Please do not change this file!
3
+ interface CssExports {
4
+ 'headline': string;
5
+ 'headlineCyan': string;
6
+ 'headlineParagraph': string;
7
+ 'root': string;
8
+ }
9
+ export const cssExports: CssExports;
10
+ export default cssExports;
@@ -2,6 +2,7 @@
2
2
  height 100vh
3
3
  display flex
4
4
  width 100%
5
+ background-color var(--background)
5
6
 
6
7
  .leftPanel
7
8
  display none
@@ -9,6 +10,8 @@
9
10
  @media (min-width: 768px)
10
11
  display flex
11
12
  width 50%
13
+ height calc(100% - var(--p-6))
14
+ margin var(--p-3) 0 var(--p-3) var(--p-3)
12
15
  position relative
13
16
  overflow hidden
14
17
  background-color var(--secondary)
@@ -20,15 +23,23 @@
20
23
  :global(.dark) &
21
24
  background-color var(--page-color-alpha-800)
22
25
 
23
- .bgImage
26
+ // ~500px graphic, -200px offset → ~300px (~60%) visible inside panel (clipped by .leftPanel overflow)
27
+ .heroWatermark
24
28
  position absolute
25
- bottom 0
26
- left 0
27
- width 300px
28
- height 300px
29
- background-size contain
30
- background-repeat no-repeat
31
- background-position left bottom
29
+ bottom -180px
30
+ left -180px
31
+ width 500px
32
+ height 500px
33
+ align-items flex-end
34
+ justify-content flex-start
35
+ gap 0
36
+ pointer-events none
37
+
38
+ img
39
+ width 100% !important
40
+ height 100% !important
41
+ object-fit contain
42
+ object-position left bottom
32
43
 
33
44
  .logoContainer
34
45
  z-index 10
@@ -45,8 +56,8 @@
45
56
  height 24px
46
57
 
47
58
  .rightPanel
59
+ position relative
48
60
  flex 1
49
- background-color var(--background)
50
61
  display flex
51
62
  flex-direction column
52
63
  justify-content center
@@ -1,2 +1,16 @@
1
- const mod: { [cls: string]: string };
2
- export default mod;
1
+ // This file is automatically generated.
2
+ // Please do not change this file!
3
+ interface CssExports {
4
+ 'formContainer': string;
5
+ 'header': string;
6
+ 'heroWatermark': string;
7
+ 'leftPanel': string;
8
+ 'logo': string;
9
+ 'logoContainer': string;
10
+ 'rightPanel': string;
11
+ 'root': string;
12
+ 'subtitle': string;
13
+ 'title': string;
14
+ }
15
+ export const cssExports: CssExports;
16
+ export default cssExports;
@@ -7,18 +7,13 @@ import type { LogoSize } from '#uilib/components/ui/Logo/Logo.types';
7
7
  import { SybilionAuthHeadline } from './SybilionAuthHeadline';
8
8
  import S from './SybilionAuthLayout.styl';
9
9
 
10
- /** Same convention as {@link SYBILION_STANDALONE_LOGO_PUBLIC_URL}: copy `sybilion-bg.svg` from the package into `public/`. */
11
- export const SYBILION_STANDALONE_AUTH_HERO_BG_PUBLIC_URL =
12
- '/sybilion_bg.svg' as const;
13
-
14
10
  export type SybilionAuthLayoutProps = {
15
11
  title: string;
16
12
  subtitle?: string;
17
13
  children: ReactNode;
18
14
  logoSize?: LogoSize;
19
15
  containerClassName?: string;
20
- /** Public URL for the hero watermark SVG (default {@link SYBILION_STANDALONE_AUTH_HERO_BG_PUBLIC_URL}). */
21
- heroBackgroundUrl?: string;
16
+ style?: React.CSSProperties;
22
17
  };
23
18
 
24
19
  export function SybilionAuthLayout({
@@ -27,20 +22,12 @@ export function SybilionAuthLayout({
27
22
  children,
28
23
  logoSize,
29
24
  containerClassName,
30
- heroBackgroundUrl = SYBILION_STANDALONE_AUTH_HERO_BG_PUBLIC_URL,
25
+ style,
31
26
  }: SybilionAuthLayoutProps) {
32
- const bg = heroBackgroundUrl
33
- ? `url(${JSON.stringify(heroBackgroundUrl)})`
34
- : 'none';
35
-
36
27
  return (
37
- <div className={S.root}>
28
+ <div className={S.root} style={style}>
38
29
  <div className={S.leftPanel}>
39
- <div
40
- className={S.bgImage}
41
- style={{ backgroundImage: bg }}
42
- aria-hidden
43
- />
30
+ <Logo showText={false} className={S.heroWatermark} aria-hidden />
44
31
 
45
32
  <div className={S.logoContainer}>
46
33
  <Logo className={S.logo} size={logoSize} />
@@ -1,6 +1,5 @@
1
1
  export {
2
2
  SybilionAuthLayout,
3
- SYBILION_STANDALONE_AUTH_HERO_BG_PUBLIC_URL,
4
3
  type SybilionAuthLayoutProps,
5
4
  } from './SybilionAuthLayout';
6
5
  export { SybilionAuthHeadline } from './SybilionAuthHeadline';
@@ -35,6 +35,9 @@
35
35
  opacity 0.8
36
36
 
37
37
  .version
38
+ position absolute
39
+ bottom 0
40
+ right 0
38
41
  display block
39
42
  margin-top var(--p-8)
40
43
  padding-bottom var(--p-2)
@@ -1,2 +1,12 @@
1
- const mod: { [cls: string]: string };
2
- export default mod;
1
+ // This file is automatically generated.
2
+ // Please do not change this file!
3
+ interface CssExports {
4
+ 'errorMessage': string;
5
+ 'forgotPassword': string;
6
+ 'forgotPasswordLink': string;
7
+ 'socialButton': string;
8
+ 'socialButtonContainer': string;
9
+ 'version': string;
10
+ }
11
+ export const cssExports: CssExports;
12
+ export default cssExports;