@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
@@ -0,0 +1,106 @@
1
+ import {
2
+ createContext,
3
+ useCallback,
4
+ useContext,
5
+ useEffect,
6
+ useMemo,
7
+ useState,
8
+ } from 'react';
9
+
10
+ import { Theme as ThemeRoot } from '@homecode/ui';
11
+
12
+ import {
13
+ getThemeConfig as defaultGetThemeConfig,
14
+ type GetThemeConfigOptions,
15
+ } from '#uilib/docs/lib/theme';
16
+
17
+ export type ThemeMode = 'light' | 'dark';
18
+
19
+ export type GetThemeConfigFn = (
20
+ isDarkTheme: boolean,
21
+ options?: GetThemeConfigOptions,
22
+ ) => ReturnType<typeof defaultGetThemeConfig>;
23
+
24
+ export type { GetThemeConfigOptions };
25
+
26
+ const ThemeContext = createContext<{
27
+ theme: ThemeMode;
28
+ isDarkMode: boolean;
29
+ setTheme: (theme: ThemeMode) => void;
30
+ toggleTheme: () => void;
31
+ }>({
32
+ theme: 'light',
33
+ isDarkMode: false,
34
+ setTheme: () => {},
35
+ toggleTheme: () => {},
36
+ });
37
+
38
+ export type ThemeProviderProps = {
39
+ children: React.ReactNode;
40
+ /** When false, DOM classes still update but theme is not persisted to `localStorage`. */
41
+ allowLocalStorage?: boolean;
42
+ /** Override docs default active/accent token (`DEFAULT_THEME_ACTIVE_COLOR`). Passed into `getThemeConfig`. */
43
+ activeColor?: string;
44
+ /** Homecode theme config; defaults to uilib docs palette. */
45
+ getThemeConfig?: GetThemeConfigFn;
46
+ };
47
+
48
+ export function ThemeProvider({
49
+ children,
50
+ allowLocalStorage = true,
51
+ activeColor,
52
+ getThemeConfig: getThemeConfigProp,
53
+ }: ThemeProviderProps) {
54
+ const getThemeConfig = getThemeConfigProp ?? defaultGetThemeConfig;
55
+
56
+ const themeConfigOptions = useMemo<GetThemeConfigOptions>(
57
+ () => ({ activeColor }),
58
+ [activeColor],
59
+ );
60
+
61
+ const [theme, setTheme] = useState<ThemeMode>(() => {
62
+ return (localStorage.getItem('theme') as ThemeMode) || 'light';
63
+ });
64
+
65
+ const [currThemeConfig, setCurrThemeConfig] = useState(() =>
66
+ getThemeConfig(theme === 'dark', themeConfigOptions),
67
+ );
68
+
69
+ const toggleTheme = useCallback(() => {
70
+ setTheme(t => (t === 'dark' ? 'light' : 'dark'));
71
+ }, []);
72
+
73
+ useEffect(() => {
74
+ setCurrThemeConfig(getThemeConfig(theme === 'dark', themeConfigOptions));
75
+ }, [theme, getThemeConfig, themeConfigOptions]);
76
+
77
+ useEffect(() => {
78
+ const root = document.documentElement;
79
+ const effectiveTheme = theme;
80
+
81
+ root.classList.remove('light', 'dark');
82
+ root.classList.add(effectiveTheme);
83
+ if (allowLocalStorage) {
84
+ localStorage.setItem('theme', theme);
85
+ }
86
+ }, [theme, allowLocalStorage]);
87
+
88
+ const value = useMemo(
89
+ () => ({
90
+ theme,
91
+ isDarkMode: theme === 'dark',
92
+ setTheme,
93
+ toggleTheme,
94
+ }),
95
+ [theme, toggleTheme],
96
+ );
97
+
98
+ return (
99
+ <ThemeContext.Provider value={value}>
100
+ <ThemeRoot config={currThemeConfig} />
101
+ {children}
102
+ </ThemeContext.Provider>
103
+ );
104
+ }
105
+
106
+ export const useTheme = () => useContext(ThemeContext);
@@ -1,5 +1,5 @@
1
1
  import { Button } from '#uilib/components/ui/Button';
2
- import { useTheme } from '#uilib/docs/contexts/theme-context';
2
+ import { useTheme } from '#uilib/contexts/theme-context';
3
3
  import { MoonIcon, SunIcon } from '@phosphor-icons/react';
4
4
 
5
5
  export function ThemeToggle() {
@@ -1,10 +1,10 @@
1
- import { Outlet } from 'react-router-dom';
1
+ import { Outlet, useLocation, useNavigate } from 'react-router-dom';
2
2
 
3
- import { AppHeaderHost, AppHeaderPortal } from '#uilib/components/ui/AppHeader';
4
- import { Gap } from '#uilib/components/ui/Gap/Gap';
3
+ import { AppHeaderHost } from '#uilib/components/ui/AppHeader';
5
4
  import { AppShell, AppShellMainContent } from '#uilib/components/ui/Page';
6
5
  import { PageFooter } from '#uilib/components/ui/Page/PageFooter/PageFooter';
7
6
  import { PageScroll } from '#uilib/components/ui/Page/PageScroll/PageScroll';
7
+ import { SybilionAppHeader } from '#uilib/components/widgets/SybilionAppHeader';
8
8
  import { ThemeToggle } from '#uilib/docs/App/ThemeToggle';
9
9
  import { DocsSidebar } from '#uilib/docs/components/DocsSidebar/DocsSidebar';
10
10
 
@@ -12,6 +12,9 @@ import LogoSvg from '../../assets/logo.svg';
12
12
  import S from './DocsShell.styl';
13
13
 
14
14
  export function DocsShell() {
15
+ const location = useLocation();
16
+ const navigate = useNavigate();
17
+
15
18
  return (
16
19
  <PageScroll className={S.root}>
17
20
  <AppShell>
@@ -27,10 +30,16 @@ export function DocsShell() {
27
30
  />
28
31
  }
29
32
  >
30
- <AppHeaderPortal>
31
- <Gap />
32
- <ThemeToggle />
33
- </AppHeaderPortal>
33
+ <SybilionAppHeader
34
+ pathname={location.pathname}
35
+ onNavigate={href => {
36
+ void navigate(href);
37
+ }}
38
+ authenticated={false}
39
+ isAuthenticated={false}
40
+ onLogout={() => undefined}
41
+ signInSlot={<ThemeToggle />}
42
+ />
34
43
  <Outlet />
35
44
  </AppShellMainContent>
36
45
  </AppShell>
@@ -79,6 +79,7 @@ export function DocsSidebar() {
79
79
  useEffect(() => {
80
80
  if (activeSection) {
81
81
  setExpandedSections(prev => new Set(prev).add(activeSection));
82
+ setSidebarSearch('');
82
83
  }
83
84
  }, [activeSection]);
84
85
 
@@ -1,68 +1,8 @@
1
- import {
2
- createContext,
3
- useCallback,
4
- useContext,
5
- useEffect,
6
- useState,
7
- } from 'react';
8
-
9
- import { Theme as ThemeRoot } from '@homecode/ui';
10
-
11
- import { getThemeConfig } from '../lib/theme';
12
-
13
- export type ThemeMode = 'light' | 'dark';
14
-
15
- const ThemeContext = createContext<{
16
- theme: ThemeMode;
17
- isDarkMode: boolean;
18
- setTheme: (theme: ThemeMode) => void;
19
- toggleTheme: () => void;
20
- }>({
21
- theme: 'light',
22
- isDarkMode: false,
23
- setTheme: () => {},
24
- toggleTheme: () => {},
25
- });
26
-
27
- export function ThemeProvider({ children }: { children: React.ReactNode }) {
28
- const [theme, setTheme] = useState<ThemeMode>(() => {
29
- return (localStorage.getItem('theme') as ThemeMode) || 'light';
30
- });
31
-
32
- const [currThemeConfig, setCurrThemeConfig] = useState(() =>
33
- getThemeConfig(theme === 'dark'),
34
- );
35
-
36
- const toggleTheme = useCallback(() => {
37
- setTheme(theme === 'dark' ? 'light' : 'dark');
38
- }, [theme, setTheme]);
39
-
40
- useEffect(() => {
41
- setCurrThemeConfig(getThemeConfig(theme === 'dark'));
42
- }, [theme]);
43
-
44
- useEffect(() => {
45
- const root = document.documentElement;
46
- const effectiveTheme = theme;
47
-
48
- root.classList.remove('light', 'dark');
49
- root.classList.add(effectiveTheme);
50
- localStorage.setItem('theme', theme);
51
- }, [theme]);
52
-
53
- return (
54
- <ThemeContext.Provider
55
- value={{
56
- theme,
57
- isDarkMode: theme === 'dark',
58
- setTheme,
59
- toggleTheme,
60
- }}
61
- >
62
- <ThemeRoot config={currThemeConfig} />
63
- {children}
64
- </ThemeContext.Provider>
65
- );
66
- }
67
-
68
- export const useTheme = () => useContext(ThemeContext);
1
+ export {
2
+ ThemeProvider,
3
+ useTheme,
4
+ type GetThemeConfigFn,
5
+ type GetThemeConfigOptions,
6
+ type ThemeMode,
7
+ type ThemeProviderProps,
8
+ } from '#uilib/contexts/theme-context';
@@ -9,7 +9,7 @@ const root = createRoot(elem);
9
9
 
10
10
  root.render(
11
11
  <BrowserRouter>
12
- <ThemeProvider>
12
+ <ThemeProvider activeColor="#">
13
13
  <App />
14
14
  </ThemeProvider>
15
15
  </BrowserRouter>,
@@ -7,6 +7,12 @@ const defaultConfig = getConfig();
7
7
 
8
8
  const alphaMods = [0, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900];
9
9
 
10
+ export const DEFAULT_THEME_ACTIVE_COLOR = '#9c59ff';
11
+
12
+ export type GetThemeConfigOptions = {
13
+ activeColor?: string;
14
+ };
15
+
10
16
  export const colorsConfig = {
11
17
  light: {
12
18
  ...ThemeHelpers.colorsConfigToVars({
@@ -26,13 +32,18 @@ export const colorsConfig = {
26
32
  },
27
33
  };
28
34
 
29
- export function getThemeConfig(isDarkTheme: boolean) {
35
+ export function getThemeConfig(
36
+ isDarkTheme: boolean,
37
+ options?: GetThemeConfigOptions,
38
+ ) {
39
+ const activeColor = options?.activeColor ?? DEFAULT_THEME_ACTIVE_COLOR;
40
+
30
41
  return {
31
42
  ...defaultConfig,
32
43
  ...colorsConfig[isDarkTheme ? 'dark' : 'light'],
33
44
  ...ThemeHelpers.colorsConfigToVars({
34
45
  active: {
35
- color: '#9c59ff',
46
+ color: activeColor,
36
47
  mods: {
37
48
  // @ts-ignore
38
49
  alpha: alphaMods,
@@ -9,7 +9,7 @@ import type {
9
9
  import { ForecastItemData } from '#uilib/components/ui/ChartAreaInteractive/ChartLines';
10
10
  import { PageContentSection } from '#uilib/components/ui/Page';
11
11
  import { Tabs, TabsList, TabsTrigger } from '#uilib/components/ui/Tabs';
12
- import { useTheme } from '#uilib/docs/contexts/theme-context';
12
+ import { useTheme } from '#uilib/contexts/theme-context';
13
13
  import type { ForecastData } from '#uilib/types/forecast-data';
14
14
 
15
15
  import { AppPageHeader } from '../components/AppPageHeader/AppPageHeader';
@@ -84,7 +84,7 @@ const INITIAL_CHART: ChartDataPoint[] = [
84
84
  ];
85
85
 
86
86
  const DEMO_FORECAST_ITEMS: ForecastItemData[] = [
87
- { id: DEMO_FORECAST_ID, name: 'Forecast' },
87
+ { id: DEMO_FORECAST_ID, name: 'My custom forecast' },
88
88
  ];
89
89
 
90
90
  type DemoMode = 'none' | OverlayMode;
@@ -29,7 +29,7 @@
29
29
  min-height 100vh
30
30
  margin-top var(--p-4)
31
31
  padding var(--p-4)
32
- border-radius var(--p-2)
32
+ border-radius var(--p-4)
33
33
  border 2px dashed var(--border)
34
34
  background-color var(--background)
35
35
  color var(--muted-foreground)
@@ -191,10 +191,11 @@ export default function StandaloneAppLayoutPage() {
191
191
  title="Standalone app layout"
192
192
  subheader={
193
193
  <>
194
- Live preview of AppShell + Sidebar + main column from
195
- docs/standalone-apps.md §4. <br />
196
- Full greenfield setup (deps, global CSS, SybilionAuthProvider, SDK)
197
- lives in that doc — this page is layout only.
194
+ Live preview of AppShell + Sidebar + main column.{' '}
195
+ <br />
196
+ Greenfield wiring: copy{' '}
197
+ <code>mini/sybilion-app-template/</code> and follow its README — this
198
+ page is layout only.
198
199
  </>
199
200
  }
200
201
  actions={<DocsHeaderActions />}
@@ -0,0 +1,47 @@
1
+ import { PageContentSection } from '#uilib/components/ui/Page';
2
+ import { SybilionAuthLayout } from '#uilib/components/widgets/SybilionAuthLayout';
3
+ import { SybilionSignInPanel } from '#uilib/components/widgets/SybilionSignInPanel';
4
+
5
+ import { AppPageHeader } from '../components/AppPageHeader/AppPageHeader';
6
+ import { DocsHeaderActions } from '../docsHeaderActions';
7
+
8
+ export default function SybilionAuthLayoutPage() {
9
+ return (
10
+ <>
11
+ <AppPageHeader
12
+ breadcrumbs={[{ label: 'SybilionAuthLayout' }]}
13
+ title="SybilionAuthLayout"
14
+ subheader="Split-view auth chrome (hero column + form). Standalone apps: copy package logo to public/logo.svg — see Logo."
15
+ actions={<DocsHeaderActions />}
16
+ />
17
+ <PageContentSection title="Preview">
18
+ <p style={{ marginTop: 0, color: 'var(--muted-foreground)' }}>
19
+ Clipped height so <code>100vh</code> layout fits the docs shell. Real
20
+ sign-in route uses full viewport — e.g. <code>SignInPage</code> (wraps
21
+ this layout + <code>SybilionSignInPanel</code>).
22
+ </p>
23
+ <div
24
+ style={{
25
+ overflow: 'hidden',
26
+ borderRadius: 34,
27
+ border: '2px dashed var(--border)',
28
+ height: 'min(720px, 85vh)',
29
+ }}
30
+ >
31
+ <SybilionAuthLayout
32
+ title="Sign In"
33
+ subtitle="Example — authenticate to continue."
34
+ style={{ height: '100%' }}
35
+ >
36
+ <SybilionSignInPanel
37
+ onSignIn={() => undefined}
38
+ forgotPasswordTo="/forgot-password"
39
+ releasesTo="/releases"
40
+ versionLabel="0.0.1"
41
+ />
42
+ </SybilionAuthLayout>
43
+ </div>
44
+ </PageContentSection>
45
+ </>
46
+ );
47
+ }
@@ -163,10 +163,10 @@ export const DOC_REGISTRY: DocEntry[] = [
163
163
  load: () => import('./pages/InteractiveContentPage'),
164
164
  },
165
165
  {
166
- slug: 'sybilion-auth-provider',
167
- title: 'SybilionAuthProvider',
166
+ slug: 'sybilion-auth-layout',
167
+ title: 'SybilionAuthLayout',
168
168
  section: 'Layout',
169
- load: () => import('./pages/SybilionAuthProviderPage'),
169
+ load: () => import('./pages/SybilionAuthLayoutPage'),
170
170
  },
171
171
  {
172
172
  slug: 'label',
package/src/index.ts CHANGED
@@ -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';
@@ -57,7 +57,16 @@ export type SybilionAuthProviderProps = {
57
57
  redirect_uri?: string;
58
58
  };
59
59
  sybilionTokenStorageKey?: string;
60
+ /**
61
+ * When set, passed to Auth0 `logout({ logoutParams: { returnTo } })` if
62
+ * {@link SybilionAuthProviderProps.useFederatedLogout} is true.
63
+ */
60
64
  logoutReturnTo?: string;
65
+ /**
66
+ * If true, redirect to Auth0 `/v2/logout` (clears IdP session). Default false matches
67
+ * sybilion-client: local session clear only, no redirect (`openUrl: false`).
68
+ */
69
+ useFederatedLogout?: boolean;
61
70
  };
62
71
 
63
72
  export type SybilionAuthContextValue = {
@@ -144,11 +153,13 @@ function InnerSybilionSession({
144
153
  sdk,
145
154
  storageKey,
146
155
  logoutReturnTo,
156
+ useFederatedLogout = false,
147
157
  }: {
148
158
  children: ReactNode;
149
159
  sdk: SybilionSDK;
150
160
  storageKey: string;
151
161
  logoutReturnTo?: string;
162
+ useFederatedLogout?: boolean;
152
163
  }): JSX.Element {
153
164
  const auth0 = useAuth0();
154
165
  const auth0Ref = useRef(auth0);
@@ -157,6 +168,7 @@ function InnerSybilionSession({
157
168
  const [sybilionToken, setSybilionToken] = useState<string | null>(null);
158
169
  const [exchangeLoading, setExchangeLoading] = useState(false);
159
170
  const [exchangeError, setExchangeError] = useState<string | null>(null);
171
+ const [isLoggingOut, setIsLoggingOut] = useState(false);
160
172
 
161
173
  const persistToken = useCallback(
162
174
  (t: string | null) => {
@@ -167,15 +179,26 @@ function InnerSybilionSession({
167
179
  );
168
180
 
169
181
  const doLogout = useCallback(() => {
182
+ setIsLoggingOut(true);
170
183
  persistToken(null);
171
184
  setExchangeError(null);
172
- const returnTo =
173
- logoutReturnTo ??
174
- (typeof window !== 'undefined' ? window.location.origin : undefined);
175
- auth0Ref.current.logout({
176
- logoutParams: returnTo ? { returnTo } : undefined,
177
- });
178
- }, [persistToken, logoutReturnTo]);
185
+ if (useFederatedLogout) {
186
+ const returnTo =
187
+ logoutReturnTo ??
188
+ (typeof window !== 'undefined' ? window.location.origin : undefined);
189
+ void auth0Ref.current.logout({
190
+ logoutParams: returnTo ? { returnTo } : undefined,
191
+ });
192
+ } else {
193
+ void auth0Ref.current.logout({ openUrl: false });
194
+ }
195
+ }, [persistToken, logoutReturnTo, useFederatedLogout]);
196
+
197
+ useEffect(() => {
198
+ if (!auth0.isAuthenticated) {
199
+ setIsLoggingOut(false);
200
+ }
201
+ }, [auth0.isAuthenticated]);
179
202
 
180
203
  const apiBaseUrl = useMemo(() => getSybilionApiOriginFromSdk(sdk), [sdk]);
181
204
 
@@ -255,7 +278,8 @@ function InnerSybilionSession({
255
278
  exchangeLoading ||
256
279
  (Boolean(auth0.isAuthenticated && auth0.user) &&
257
280
  sybilionToken === null &&
258
- exchangeError === null);
281
+ exchangeError === null &&
282
+ !isLoggingOut);
259
283
 
260
284
  const value = useMemo(
261
285
  (): SybilionAuthContextValue => ({
@@ -299,6 +323,7 @@ export function SybilionAuthProvider({
299
323
  authorizationParams,
300
324
  sybilionTokenStorageKey = DEFAULT_TOKEN_KEY,
301
325
  logoutReturnTo,
326
+ useFederatedLogout = false,
302
327
  }: SybilionAuthProviderProps): JSX.Element {
303
328
  const mergedAuthParams = useMemo(
304
329
  () => ({
@@ -336,6 +361,7 @@ export function SybilionAuthProvider({
336
361
  sdk={sdk}
337
362
  storageKey={sybilionTokenStorageKey}
338
363
  logoutReturnTo={logoutReturnTo}
364
+ useFederatedLogout={useFederatedLogout}
339
365
  >
340
366
  {children}
341
367
  </InnerSybilionSession>