@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.
- package/README.md +10 -7
- package/dist/esm/components/ui/AppHeader/AppHeader.styl.js +1 -1
- package/dist/esm/components/ui/Logo/Logo.js +2 -1
- package/dist/esm/components/ui/NavUserHeader/NavUserHeader.js +19 -6
- package/dist/esm/components/ui/NavUserHeader/NavUserHeader.styl.js +2 -2
- package/dist/esm/components/ui/Sidebar/Sidebar.styl.js +1 -1
- package/dist/esm/components/widgets/SidebarDatasetsItemsGrouped/SidebarDatasetsItemsGrouped.js +8 -2
- package/dist/esm/components/widgets/SignInPage/SignInPage.js +2 -2
- package/dist/esm/components/widgets/SybilionAppHeader/SybilionAppHeader.js +2 -2
- package/dist/esm/components/widgets/SybilionAuthLayout/SybilionAuthHeadline.styl.js +1 -1
- package/dist/esm/components/widgets/SybilionAuthLayout/SybilionAuthLayout.js +3 -8
- package/dist/esm/components/widgets/SybilionAuthLayout/SybilionAuthLayout.styl.js +2 -2
- package/dist/esm/components/widgets/SybilionSignInPanel/SybilionSignInPanel.styl.js +1 -1
- package/dist/esm/contexts/theme-context.js +44 -0
- package/dist/esm/docs/lib/theme.js +35 -3
- package/dist/esm/index.js +3 -1
- package/dist/esm/sybilion-auth/SybilionAuthProvider.js +23 -10
- package/dist/esm/types/src/components/ui/Logo/Logo.d.ts +2 -1
- 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 +3 -0
- package/dist/esm/types/src/components/widgets/SidebarDatasetsItemsGrouped/SidebarDatasetsItemsGrouped.d.ts +4 -1
- package/dist/esm/types/src/components/widgets/SignInPage/SignInPage.d.ts +2 -2
- package/dist/esm/types/src/components/widgets/SybilionAppHeader/SybilionAppHeader.d.ts +5 -1
- package/dist/esm/types/src/components/widgets/SybilionAuthLayout/SybilionAuthLayout.d.ts +2 -5
- package/dist/esm/types/src/components/widgets/SybilionAuthLayout/index.d.ts +1 -1
- package/dist/esm/types/src/contexts/theme-context.d.ts +20 -0
- package/dist/esm/types/src/docs/contexts/theme-context.d.ts +1 -10
- package/dist/esm/types/src/docs/lib/theme.d.ts +5 -1
- package/dist/esm/types/src/docs/pages/SybilionAuthLayoutPage.d.ts +1 -0
- package/dist/esm/types/src/index.d.ts +2 -0
- package/dist/esm/types/src/sybilion-auth/SybilionAuthProvider.d.ts +10 -1
- package/package.json +2 -4
- package/src/components/ui/AppHeader/AppHeader.styl +2 -0
- package/src/components/ui/Logo/Logo.tsx +2 -1
- package/src/components/ui/NavUserHeader/NavUserHeader.styl +2 -20
- package/src/components/ui/NavUserHeader/NavUserHeader.styl.d.ts +0 -3
- package/src/components/ui/NavUserHeader/NavUserHeader.tsx +40 -30
- package/src/components/ui/NavUserHeader/NavUserHeader.types.ts +3 -0
- package/src/components/ui/Sidebar/Sidebar.styl +2 -2
- package/src/components/widgets/SidebarDatasetsItemsGrouped/SidebarDatasetsItemsGrouped.tsx +14 -3
- package/src/components/widgets/SignInPage/SignInPage.tsx +1 -3
- package/src/components/widgets/SybilionAppHeader/SybilionAppHeader.tsx +8 -0
- package/src/components/widgets/SybilionAuthLayout/SybilionAuthHeadline.styl +2 -2
- package/src/components/widgets/SybilionAuthLayout/SybilionAuthHeadline.styl.d.ts +10 -2
- package/src/components/widgets/SybilionAuthLayout/SybilionAuthLayout.styl +20 -9
- package/src/components/widgets/SybilionAuthLayout/SybilionAuthLayout.styl.d.ts +16 -2
- package/src/components/widgets/SybilionAuthLayout/SybilionAuthLayout.tsx +4 -17
- package/src/components/widgets/SybilionAuthLayout/index.ts +0 -1
- package/src/components/widgets/SybilionSignInPanel/SybilionSignInPanel.styl +3 -0
- package/src/components/widgets/SybilionSignInPanel/SybilionSignInPanel.styl.d.ts +12 -2
- package/src/contexts/theme-context.tsx +106 -0
- package/src/docs/App/ThemeToggle.tsx +1 -1
- package/src/docs/DocsShell.tsx +16 -7
- package/src/docs/components/DocsSidebar/DocsSidebar.tsx +1 -0
- package/src/docs/contexts/theme-context.tsx +8 -68
- package/src/docs/index.tsx +1 -1
- package/src/docs/lib/theme.ts +13 -2
- package/src/docs/pages/ChartAreaInteractivePage.tsx +2 -2
- package/src/docs/pages/StandaloneAppLayoutPage/StandaloneAppLayoutPage.styl +1 -1
- package/src/docs/pages/StandaloneAppLayoutPage/StandaloneAppLayoutPage.tsx +5 -4
- package/src/docs/pages/SybilionAuthLayoutPage.tsx +47 -0
- package/src/docs/registry.ts +3 -3
- package/src/index.ts +2 -0
- package/src/sybilion-auth/SybilionAuthProvider.tsx +34 -8
- package/assets/standalone-global.css +0 -257
- package/dist/esm/docs/contexts/theme-context.js +0 -14
- package/dist/esm/types/src/docs/pages/SybilionAuthProviderPage.d.ts +0 -1
- package/docs/standalone-apps.md +0 -552
- 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/
|
|
2
|
+
import { useTheme } from '#uilib/contexts/theme-context';
|
|
3
3
|
import { MoonIcon, SunIcon } from '@phosphor-icons/react';
|
|
4
4
|
|
|
5
5
|
export function ThemeToggle() {
|
package/src/docs/DocsShell.tsx
CHANGED
|
@@ -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
|
|
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
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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>
|
|
@@ -1,68 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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';
|
package/src/docs/index.tsx
CHANGED
package/src/docs/lib/theme.ts
CHANGED
|
@@ -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(
|
|
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:
|
|
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/
|
|
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: '
|
|
87
|
+
{ id: DEMO_FORECAST_ID, name: 'My custom forecast' },
|
|
88
88
|
];
|
|
89
89
|
|
|
90
90
|
type DemoMode = 'none' | OverlayMode;
|
|
@@ -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
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
+
}
|
package/src/docs/registry.ts
CHANGED
|
@@ -163,10 +163,10 @@ export const DOC_REGISTRY: DocEntry[] = [
|
|
|
163
163
|
load: () => import('./pages/InteractiveContentPage'),
|
|
164
164
|
},
|
|
165
165
|
{
|
|
166
|
-
slug: 'sybilion-auth-
|
|
167
|
-
title: '
|
|
166
|
+
slug: 'sybilion-auth-layout',
|
|
167
|
+
title: 'SybilionAuthLayout',
|
|
168
168
|
section: 'Layout',
|
|
169
|
-
load: () => import('./pages/
|
|
169
|
+
load: () => import('./pages/SybilionAuthLayoutPage'),
|
|
170
170
|
},
|
|
171
171
|
{
|
|
172
172
|
slug: 'label',
|
package/src/index.ts
CHANGED
|
@@ -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
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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>
|