@sybilion/uilib 1.2.10 → 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 +11 -3
- 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/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/index.js +1 -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/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/SybilionAuthLayout/SybilionAuthLayout.d.ts +2 -5
- package/dist/esm/types/src/components/widgets/SybilionAuthLayout/index.d.ts +1 -1
- package/dist/esm/types/src/docs/pages/SybilionAuthLayoutPage.d.ts +1 -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 +30 -28
- 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/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 +1 -1
- package/src/components/widgets/SybilionSignInPanel/SybilionSignInPanel.styl.d.ts +12 -2
- package/src/docs/DocsShell.tsx +16 -7
- package/src/docs/components/DocsSidebar/DocsSidebar.tsx +1 -0
- package/src/docs/pages/ChartAreaInteractivePage.tsx +1 -1
- 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/sybilion-auth/SybilionAuthProvider.tsx +34 -8
- package/assets/standalone-global.css +0 -257
- package/dist/esm/types/src/docs/pages/SybilionAuthProviderPage.d.ts +0 -1
- package/docs/standalone-apps.md +0 -553
- package/src/docs/pages/SybilionAuthProviderPage.tsx +0 -40
|
@@ -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
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import cn from 'classnames';
|
|
2
|
+
import { useMemo } from 'react';
|
|
2
3
|
|
|
3
4
|
import { useTheme } from '#uilib/contexts/theme-context';
|
|
4
5
|
import {
|
|
@@ -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
|
|
|
@@ -49,6 +49,30 @@ export function NavUserHeader({
|
|
|
49
49
|
const userName = user?.name ?? '';
|
|
50
50
|
const userEmail = user?.email ?? '';
|
|
51
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
|
+
|
|
52
76
|
if (isLoading) {
|
|
53
77
|
return (
|
|
54
78
|
<Button variant="ghost" size="sm" disabled className={S.loadingButton}>
|
|
@@ -84,21 +108,10 @@ export function NavUserHeader({
|
|
|
84
108
|
size="sm"
|
|
85
109
|
className={cn(S.userButton, variant === 'compact' && S.compact)}
|
|
86
110
|
>
|
|
87
|
-
|
|
88
|
-
<Image
|
|
89
|
-
url={avatarUrl}
|
|
90
|
-
alt={userName}
|
|
91
|
-
fallback={<div className={S.avatarFallback} />}
|
|
92
|
-
/>
|
|
93
|
-
</Avatar>
|
|
111
|
+
{avatar}
|
|
94
112
|
{variant === 'default' && (
|
|
95
113
|
<>
|
|
96
|
-
|
|
97
|
-
<span className={`${S.userName} ph-no-capture`}>
|
|
98
|
-
{userName}
|
|
99
|
-
</span>
|
|
100
|
-
<span className={S.userEmail}>{userEmail}</span>
|
|
101
|
-
</div>
|
|
114
|
+
{userIdentity}
|
|
102
115
|
<ChevronDownIcon className={S.iconSm} />
|
|
103
116
|
</>
|
|
104
117
|
)}
|
|
@@ -111,19 +124,8 @@ export function NavUserHeader({
|
|
|
111
124
|
>
|
|
112
125
|
<DropdownMenuLabel className={S.userLabel}>
|
|
113
126
|
<div className={S.userLabelContent}>
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
url={avatarUrl}
|
|
117
|
-
alt={userName}
|
|
118
|
-
fallback={<div className={S.avatarFallback} />}
|
|
119
|
-
/>
|
|
120
|
-
</Avatar>
|
|
121
|
-
<div className={S.userDetails}>
|
|
122
|
-
<span className={`${S.userDetailName} ph-no-capture`}>
|
|
123
|
-
{userName}
|
|
124
|
-
</span>
|
|
125
|
-
<span className={S.userDetailEmail}>{userEmail}</span>
|
|
126
|
-
</div>
|
|
127
|
+
{avatar}
|
|
128
|
+
{userIdentity}
|
|
127
129
|
</div>
|
|
128
130
|
</DropdownMenuLabel>
|
|
129
131
|
<DropdownMenuSeparator />
|
|
@@ -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
|
-
|
|
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
|
-
/**
|
|
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(
|
|
64
|
+
setExpanded(new Set());
|
|
56
65
|
}
|
|
57
|
-
|
|
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
|
-
'
|
|
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
|
>
|
|
@@ -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
|
|
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
|
|
26
|
+
color var(--brand-color)
|
|
@@ -1,2 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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
|
-
.
|
|
26
|
+
// ~500px graphic, -200px offset → ~300px (~60%) visible inside panel (clipped by .leftPanel overflow)
|
|
27
|
+
.heroWatermark
|
|
24
28
|
position absolute
|
|
25
|
-
bottom
|
|
26
|
-
left
|
|
27
|
-
width
|
|
28
|
-
height
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
2
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
<
|
|
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,2 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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;
|
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>
|
|
@@ -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',
|
|
@@ -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>
|