@sybilion/uilib 1.2.8 → 1.2.10
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/dist/esm/components/ui/NavUserHeader/NavUserHeader.js +8 -3
- package/dist/esm/components/widgets/SignInPage/SignInPage.js +25 -0
- package/dist/esm/components/widgets/SybilionAppHeader/SybilionAppHeader.js +2 -2
- package/dist/esm/components/widgets/SybilionAuthLayout/SybilionAuthHeadline.js +8 -0
- package/dist/esm/components/widgets/SybilionAuthLayout/SybilionAuthHeadline.styl.js +7 -0
- package/dist/esm/components/widgets/SybilionAuthLayout/SybilionAuthLayout.js +16 -0
- package/dist/esm/components/widgets/SybilionAuthLayout/SybilionAuthLayout.styl.js +7 -0
- package/dist/esm/components/widgets/SybilionSignInPanel/SybilionSignInPanel.js +11 -0
- package/dist/esm/components/widgets/SybilionSignInPanel/SybilionSignInPanel.styl.js +7 -0
- package/dist/esm/contexts/theme-context.js +44 -0
- package/dist/esm/docs/lib/theme.js +35 -3
- package/dist/esm/index.js +6 -0
- package/dist/esm/types/src/components/ui/NavUserHeader/NavUserHeader.d.ts +1 -1
- package/dist/esm/types/src/components/ui/NavUserHeader/NavUserHeader.types.d.ts +3 -0
- package/dist/esm/types/src/components/widgets/SignInPage/SignInPage.d.ts +10 -0
- package/dist/esm/types/src/components/widgets/SignInPage/index.d.ts +1 -0
- package/dist/esm/types/src/components/widgets/SybilionAppHeader/SybilionAppHeader.d.ts +5 -1
- package/dist/esm/types/src/components/widgets/SybilionAuthLayout/SybilionAuthHeadline.d.ts +1 -0
- package/dist/esm/types/src/components/widgets/SybilionAuthLayout/SybilionAuthLayout.d.ts +14 -0
- package/dist/esm/types/src/components/widgets/SybilionAuthLayout/index.d.ts +2 -0
- package/dist/esm/types/src/components/widgets/SybilionSignInPanel/SybilionSignInPanel.d.ts +11 -0
- package/dist/esm/types/src/components/widgets/SybilionSignInPanel/index.d.ts +1 -0
- 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/index.d.ts +5 -0
- package/docs/standalone-apps.md +58 -37
- package/package.json +3 -2
- package/src/assets/sybilion_bg.svg +8 -0
- package/src/components/ui/NavUserHeader/NavUserHeader.tsx +10 -2
- package/src/components/ui/NavUserHeader/NavUserHeader.types.ts +3 -0
- package/src/components/widgets/SignInPage/SignInPage.tsx +84 -0
- package/src/components/widgets/SignInPage/index.ts +1 -0
- package/src/components/widgets/SybilionAppHeader/SybilionAppHeader.tsx +8 -0
- package/src/components/widgets/SybilionAuthLayout/SybilionAuthHeadline.styl +26 -0
- package/src/components/widgets/SybilionAuthLayout/SybilionAuthHeadline.styl.d.ts +2 -0
- package/src/components/widgets/SybilionAuthLayout/SybilionAuthHeadline.tsx +18 -0
- package/src/components/widgets/SybilionAuthLayout/SybilionAuthLayout.styl +79 -0
- package/src/components/widgets/SybilionAuthLayout/SybilionAuthLayout.styl.d.ts +2 -0
- package/src/components/widgets/SybilionAuthLayout/SybilionAuthLayout.tsx +64 -0
- package/src/components/widgets/SybilionAuthLayout/index.ts +6 -0
- package/src/components/widgets/SybilionSignInPanel/SybilionSignInPanel.styl +51 -0
- package/src/components/widgets/SybilionSignInPanel/SybilionSignInPanel.styl.d.ts +2 -0
- package/src/components/widgets/SybilionSignInPanel/SybilionSignInPanel.tsx +59 -0
- package/src/components/widgets/SybilionSignInPanel/index.ts +4 -0
- package/src/contexts/theme-context.tsx +106 -0
- package/src/docs/App/ThemeToggle.tsx +1 -1
- 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 +1 -1
- package/src/index.ts +5 -0
- package/dist/esm/docs/contexts/theme-context.js +0 -14
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import cn from 'classnames';
|
|
2
2
|
|
|
3
|
-
import { useTheme } from '#uilib/
|
|
3
|
+
import { useTheme } from '#uilib/contexts/theme-context';
|
|
4
4
|
import {
|
|
5
5
|
MoonIcon,
|
|
6
6
|
SignOutIcon,
|
|
@@ -33,8 +33,16 @@ export function NavUserHeader({
|
|
|
33
33
|
onLogout,
|
|
34
34
|
signInSlot,
|
|
35
35
|
onSignInClick,
|
|
36
|
+
theme: themeFromHost,
|
|
37
|
+
onThemeToggle: onThemeToggleFromHost,
|
|
36
38
|
}: NavUserHeaderProps) {
|
|
37
|
-
const
|
|
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 ?? '';
|
|
@@ -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
|
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { RedirectLoginOptions } from '@auth0/auth0-react';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
SybilionAuthLayout,
|
|
5
|
+
type SybilionAuthLayoutProps,
|
|
6
|
+
} from '#uilib/components/widgets/SybilionAuthLayout';
|
|
7
|
+
import { useSybilionAuth } from '#uilib/sybilion-auth/SybilionAuthProvider';
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
SybilionSignInPanel,
|
|
11
|
+
type SybilionSignInPanelProps,
|
|
12
|
+
} from '../SybilionSignInPanel';
|
|
13
|
+
|
|
14
|
+
export type SignInPageProps = Pick<
|
|
15
|
+
SybilionAuthLayoutProps,
|
|
16
|
+
'heroBackgroundUrl' | 'logoSize' | 'containerClassName'
|
|
17
|
+
> &
|
|
18
|
+
Pick<
|
|
19
|
+
SybilionSignInPanelProps,
|
|
20
|
+
| 'forgotPasswordTo'
|
|
21
|
+
| 'releasesTo'
|
|
22
|
+
| 'versionLabel'
|
|
23
|
+
| 'primaryButtonLabel'
|
|
24
|
+
| 'connectingLabel'
|
|
25
|
+
> & {
|
|
26
|
+
title?: string;
|
|
27
|
+
subtitle?: string;
|
|
28
|
+
/** Extra Auth0 `loginWithRedirect` options; merged with default sign-in params. */
|
|
29
|
+
loginRedirectOptions?: RedirectLoginOptions;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const DEFAULT_TITLE = 'Sign In';
|
|
33
|
+
const DEFAULT_SUBTITLE =
|
|
34
|
+
'To get access authenticate through google or your email.';
|
|
35
|
+
|
|
36
|
+
export function SignInPage({
|
|
37
|
+
title = DEFAULT_TITLE,
|
|
38
|
+
subtitle = DEFAULT_SUBTITLE,
|
|
39
|
+
forgotPasswordTo = '/forgot-password',
|
|
40
|
+
releasesTo = '/releases',
|
|
41
|
+
versionLabel,
|
|
42
|
+
primaryButtonLabel,
|
|
43
|
+
connectingLabel,
|
|
44
|
+
loginRedirectOptions,
|
|
45
|
+
heroBackgroundUrl,
|
|
46
|
+
logoSize,
|
|
47
|
+
containerClassName,
|
|
48
|
+
}: SignInPageProps) {
|
|
49
|
+
const { error, loginWithRedirect, isLoading } = useSybilionAuth();
|
|
50
|
+
|
|
51
|
+
const handleSignIn = () =>
|
|
52
|
+
loginWithRedirect({
|
|
53
|
+
...loginRedirectOptions,
|
|
54
|
+
authorizationParams: {
|
|
55
|
+
prompt: 'login',
|
|
56
|
+
screen_hint: 'login',
|
|
57
|
+
...(typeof window !== 'undefined'
|
|
58
|
+
? { origin: window.location.origin }
|
|
59
|
+
: {}),
|
|
60
|
+
...loginRedirectOptions?.authorizationParams,
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<SybilionAuthLayout
|
|
66
|
+
title={title}
|
|
67
|
+
subtitle={subtitle}
|
|
68
|
+
heroBackgroundUrl={heroBackgroundUrl}
|
|
69
|
+
logoSize={logoSize}
|
|
70
|
+
containerClassName={containerClassName}
|
|
71
|
+
>
|
|
72
|
+
<SybilionSignInPanel
|
|
73
|
+
onSignIn={handleSignIn}
|
|
74
|
+
isSigningIn={isLoading}
|
|
75
|
+
error={error}
|
|
76
|
+
forgotPasswordTo={forgotPasswordTo}
|
|
77
|
+
releasesTo={releasesTo}
|
|
78
|
+
versionLabel={versionLabel}
|
|
79
|
+
primaryButtonLabel={primaryButtonLabel}
|
|
80
|
+
connectingLabel={connectingLabel}
|
|
81
|
+
/>
|
|
82
|
+
</SybilionAuthLayout>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SignInPage, type SignInPageProps } from './SignInPage';
|
|
@@ -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
|
);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
.root
|
|
2
|
+
display flex
|
|
3
|
+
flex-direction column
|
|
4
|
+
justify-content center
|
|
5
|
+
align-items center
|
|
6
|
+
width 100%
|
|
7
|
+
padding 64px 48px
|
|
8
|
+
position relative
|
|
9
|
+
z-index 10
|
|
10
|
+
height 100%
|
|
11
|
+
font-family var(--font-family-heading)
|
|
12
|
+
font-weight 300
|
|
13
|
+
text-shadow 0 0 2px var(--background)
|
|
14
|
+
|
|
15
|
+
.headline
|
|
16
|
+
font-size 40px
|
|
17
|
+
line-height 48px
|
|
18
|
+
text-align left
|
|
19
|
+
width 100%
|
|
20
|
+
max-width 480px
|
|
21
|
+
|
|
22
|
+
.headlineParagraph
|
|
23
|
+
margin 0
|
|
24
|
+
|
|
25
|
+
.headlineCyan
|
|
26
|
+
color #27d1ef
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import S from './SybilionAuthHeadline.styl';
|
|
2
|
+
|
|
3
|
+
export function SybilionAuthHeadline() {
|
|
4
|
+
return (
|
|
5
|
+
<div className={S.root}>
|
|
6
|
+
<div className={S.headline}>
|
|
7
|
+
<p className={S.headlineParagraph}>
|
|
8
|
+
<span>External volatility</span>
|
|
9
|
+
<span></span>
|
|
10
|
+
</p>
|
|
11
|
+
<p className={S.headlineParagraph}>turned into</p>
|
|
12
|
+
<p className={S.headlineParagraph}>
|
|
13
|
+
<span className={S.headlineCyan}>confident decisions</span>
|
|
14
|
+
</p>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
.root
|
|
2
|
+
height 100vh
|
|
3
|
+
display flex
|
|
4
|
+
width 100%
|
|
5
|
+
|
|
6
|
+
.leftPanel
|
|
7
|
+
display none
|
|
8
|
+
|
|
9
|
+
@media (min-width: 768px)
|
|
10
|
+
display flex
|
|
11
|
+
width 50%
|
|
12
|
+
position relative
|
|
13
|
+
overflow hidden
|
|
14
|
+
background-color var(--secondary)
|
|
15
|
+
border-top-left-radius var(--p-6)
|
|
16
|
+
border-bottom-left-radius var(--p-6)
|
|
17
|
+
border-top-right-radius var(--p-1)
|
|
18
|
+
border-bottom-right-radius var(--p-1)
|
|
19
|
+
|
|
20
|
+
:global(.dark) &
|
|
21
|
+
background-color var(--page-color-alpha-800)
|
|
22
|
+
|
|
23
|
+
.bgImage
|
|
24
|
+
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
|
|
32
|
+
|
|
33
|
+
.logoContainer
|
|
34
|
+
z-index 10
|
|
35
|
+
position absolute
|
|
36
|
+
top 0
|
|
37
|
+
left 0
|
|
38
|
+
display flex
|
|
39
|
+
align-items center
|
|
40
|
+
min-height 94px
|
|
41
|
+
padding 16px 48px
|
|
42
|
+
|
|
43
|
+
.logo
|
|
44
|
+
width 24px
|
|
45
|
+
height 24px
|
|
46
|
+
|
|
47
|
+
.rightPanel
|
|
48
|
+
flex 1
|
|
49
|
+
background-color var(--background)
|
|
50
|
+
display flex
|
|
51
|
+
flex-direction column
|
|
52
|
+
justify-content center
|
|
53
|
+
align-items center
|
|
54
|
+
padding 48px 64px
|
|
55
|
+
|
|
56
|
+
.formContainer
|
|
57
|
+
// position relative
|
|
58
|
+
max-width 480px
|
|
59
|
+
width 100%
|
|
60
|
+
|
|
61
|
+
.header
|
|
62
|
+
margin-bottom 36px
|
|
63
|
+
|
|
64
|
+
.title
|
|
65
|
+
font-family var(--font-family-heading)
|
|
66
|
+
font-weight normal
|
|
67
|
+
font-size 30px
|
|
68
|
+
line-height 42px
|
|
69
|
+
margin 0
|
|
70
|
+
margin-bottom 6px
|
|
71
|
+
color var(--foreground)
|
|
72
|
+
|
|
73
|
+
.subtitle
|
|
74
|
+
font-size 14px
|
|
75
|
+
color var(--muted-foreground)
|
|
76
|
+
line-height 16px
|
|
77
|
+
margin 0
|
|
78
|
+
font-family 'Manrope', sans-serif
|
|
79
|
+
font-weight 500
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import cn from 'classnames';
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
import { Logo } from '#uilib/components/ui/Logo/Logo';
|
|
5
|
+
import type { LogoSize } from '#uilib/components/ui/Logo/Logo.types';
|
|
6
|
+
|
|
7
|
+
import { SybilionAuthHeadline } from './SybilionAuthHeadline';
|
|
8
|
+
import S from './SybilionAuthLayout.styl';
|
|
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
|
+
export type SybilionAuthLayoutProps = {
|
|
15
|
+
title: string;
|
|
16
|
+
subtitle?: string;
|
|
17
|
+
children: ReactNode;
|
|
18
|
+
logoSize?: LogoSize;
|
|
19
|
+
containerClassName?: string;
|
|
20
|
+
/** Public URL for the hero watermark SVG (default {@link SYBILION_STANDALONE_AUTH_HERO_BG_PUBLIC_URL}). */
|
|
21
|
+
heroBackgroundUrl?: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export function SybilionAuthLayout({
|
|
25
|
+
title,
|
|
26
|
+
subtitle,
|
|
27
|
+
children,
|
|
28
|
+
logoSize,
|
|
29
|
+
containerClassName,
|
|
30
|
+
heroBackgroundUrl = SYBILION_STANDALONE_AUTH_HERO_BG_PUBLIC_URL,
|
|
31
|
+
}: SybilionAuthLayoutProps) {
|
|
32
|
+
const bg = heroBackgroundUrl
|
|
33
|
+
? `url(${JSON.stringify(heroBackgroundUrl)})`
|
|
34
|
+
: 'none';
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<div className={S.root}>
|
|
38
|
+
<div className={S.leftPanel}>
|
|
39
|
+
<div
|
|
40
|
+
className={S.bgImage}
|
|
41
|
+
style={{ backgroundImage: bg }}
|
|
42
|
+
aria-hidden
|
|
43
|
+
/>
|
|
44
|
+
|
|
45
|
+
<div className={S.logoContainer}>
|
|
46
|
+
<Logo className={S.logo} size={logoSize} />
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<SybilionAuthHeadline />
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<div className={S.rightPanel}>
|
|
53
|
+
<div className={cn(S.formContainer, containerClassName)}>
|
|
54
|
+
<div className={S.header}>
|
|
55
|
+
<h1 className={S.title}>{title}</h1>
|
|
56
|
+
{subtitle && <p className={S.subtitle}>{subtitle}</p>}
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
{children}
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
.socialButtonContainer
|
|
2
|
+
display flex
|
|
3
|
+
width 100%
|
|
4
|
+
gap 10px
|
|
5
|
+
margin-bottom 10px
|
|
6
|
+
|
|
7
|
+
.socialButton
|
|
8
|
+
flex 1
|
|
9
|
+
height 48px
|
|
10
|
+
padding 14px 16px
|
|
11
|
+
font-size 14px
|
|
12
|
+
font-weight 600
|
|
13
|
+
border-radius 14px
|
|
14
|
+
border 1px solid var(--border)
|
|
15
|
+
|
|
16
|
+
.errorMessage
|
|
17
|
+
color red
|
|
18
|
+
padding 10px
|
|
19
|
+
font-size 14px
|
|
20
|
+
margin-bottom 10px
|
|
21
|
+
|
|
22
|
+
.forgotPassword
|
|
23
|
+
text-align right
|
|
24
|
+
margin-bottom 24px
|
|
25
|
+
|
|
26
|
+
.forgotPasswordLink
|
|
27
|
+
color var(--primary)
|
|
28
|
+
text-decoration none
|
|
29
|
+
font-size 14px
|
|
30
|
+
font-family 'Manrope', sans-serif
|
|
31
|
+
font-weight 500
|
|
32
|
+
transition opacity 0.2s
|
|
33
|
+
|
|
34
|
+
&:hover
|
|
35
|
+
opacity 0.8
|
|
36
|
+
|
|
37
|
+
.version
|
|
38
|
+
position absolute
|
|
39
|
+
bottom 0
|
|
40
|
+
right 0
|
|
41
|
+
display block
|
|
42
|
+
margin-top var(--p-8)
|
|
43
|
+
padding-bottom var(--p-2)
|
|
44
|
+
width 50%
|
|
45
|
+
box-sizing border-box
|
|
46
|
+
text-align center
|
|
47
|
+
font-size 14px
|
|
48
|
+
color var(--muted-foreground)
|
|
49
|
+
opacity 0.5
|
|
50
|
+
transition opacity 0.3s ease-out
|
|
51
|
+
white-space nowrap
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Link } from 'react-router-dom';
|
|
2
|
+
|
|
3
|
+
import { Button } from '#uilib/components/ui/Button';
|
|
4
|
+
|
|
5
|
+
import S from './SybilionSignInPanel.styl';
|
|
6
|
+
|
|
7
|
+
export type SybilionSignInPanelProps = {
|
|
8
|
+
onSignIn: () => void | Promise<void>;
|
|
9
|
+
isSigningIn?: boolean;
|
|
10
|
+
error?: string | null;
|
|
11
|
+
forgotPasswordTo: string;
|
|
12
|
+
releasesTo?: string;
|
|
13
|
+
versionLabel?: string;
|
|
14
|
+
primaryButtonLabel?: string;
|
|
15
|
+
connectingLabel?: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export function SybilionSignInPanel({
|
|
19
|
+
onSignIn,
|
|
20
|
+
isSigningIn = false,
|
|
21
|
+
error,
|
|
22
|
+
forgotPasswordTo,
|
|
23
|
+
releasesTo,
|
|
24
|
+
versionLabel,
|
|
25
|
+
primaryButtonLabel = 'Sign-in',
|
|
26
|
+
connectingLabel = 'Connecting...',
|
|
27
|
+
}: SybilionSignInPanelProps) {
|
|
28
|
+
const signingIn = Boolean(isSigningIn);
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<>
|
|
32
|
+
<div className={S.socialButtonContainer}>
|
|
33
|
+
<Button
|
|
34
|
+
variant="outline"
|
|
35
|
+
type="button"
|
|
36
|
+
className={S.socialButton}
|
|
37
|
+
onClick={() => void onSignIn()}
|
|
38
|
+
disabled={signingIn}
|
|
39
|
+
>
|
|
40
|
+
{signingIn ? connectingLabel : primaryButtonLabel}
|
|
41
|
+
</Button>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
{error ? <div className={S.errorMessage}>{error}</div> : null}
|
|
45
|
+
|
|
46
|
+
<div className={S.forgotPassword}>
|
|
47
|
+
<Link to={forgotPasswordTo} className={S.forgotPasswordLink}>
|
|
48
|
+
Forgot password?
|
|
49
|
+
</Link>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
{releasesTo && versionLabel ? (
|
|
53
|
+
<Link className={S.version} to={releasesTo}>
|
|
54
|
+
v{versionLabel}
|
|
55
|
+
</Link>
|
|
56
|
+
) : null}
|
|
57
|
+
</>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
@@ -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() {
|
|
@@ -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';
|