fix: merge conflicts. remove import in quickstart

This commit is contained in:
Felix Dubrownik
2023-03-03 12:49:56 +01:00
105 changed files with 12072 additions and 28650 deletions

View File

@ -1,6 +1,6 @@
{
"name": "@teamhanko/hanko-elements",
"version": "0.2.0-alpha",
"version": "0.2.1-alpha",
"private": false,
"publishConfig": {
"access": "public"
@ -47,10 +47,10 @@
],
"homepage": "https://hanko.io",
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.51.0",
"@typescript-eslint/parser": "^5.53.0",
"@typescript-eslint/eslint-plugin": "^5.54.0",
"@typescript-eslint/parser": "^5.54.0",
"css-loader": "^6.7.3",
"eslint": "^8.33.0",
"eslint": "^8.35.0",
"eslint-config-google": "^0.14.0",
"eslint-config-preact": "^1.3.0",
"eslint-config-prettier": "^8.6.0",

View File

@ -5,9 +5,8 @@ import { TranslateContext } from "@denysvuika/preact-translate";
import { HankoError, TechnicalError } from "@teamhanko/hanko-frontend-sdk";
import ExclamationMark from "../icons/ExclamationMark";
import styles from "./styles.sass";
import Icon from "../icons/Icon";
type Props = {
error?: Error;
@ -28,7 +27,7 @@ const ErrorMessage = ({ error = defaultError }: Props) => {
hidden={!error}
>
<span>
<ExclamationMark />
<Icon name={"exclamation"} />
</span>
<span
id="errorMessage"

View File

@ -16,5 +16,8 @@
align-items: center
box-sizing: border-box
&>span:first-child
display: inline-flex
&[hidden]
display: none

View File

@ -7,9 +7,10 @@ import cx from "classnames";
import styles from "./styles.sass";
import LoadingSpinner from "../icons/LoadingSpinner";
import Icon, { IconName } from "../icons/Icon";
type Props = {
title?: string
title?: string;
children: ComponentChildren;
secondary?: boolean;
isLoading?: boolean;
@ -17,6 +18,7 @@ type Props = {
disabled?: boolean;
autofocus?: boolean;
onClick?: (event: Event) => void;
icon?: IconName;
};
const Button = ({
@ -28,6 +30,7 @@ const Button = ({
isSuccess,
autofocus,
onClick,
icon,
}: Props) => {
const ref = useRef(null);
@ -56,7 +59,15 @@ const Button = ({
isLoading={isLoading}
isSuccess={isSuccess}
secondary={true}
hasIcon={!!icon}
>
{icon ? (
<Icon
name={icon}
secondary={secondary}
disabled={disabled || isLoading || isSuccess}
/>
) : null}
{children}
</LoadingSpinner>
</button>

View File

@ -1,21 +1,25 @@
import * as preact from "preact";
import { IconProps } from "./Icon";
import styles from "./styles.sass";
import cx from "classnames";
import styles from "./styles.sass";
type Props = {
fadeOut?: boolean;
secondary?: boolean;
};
const Checkmark = ({ fadeOut, secondary }: Props) => {
const Checkmark = ({ secondary, size, fadeOut, disabled }: IconProps) => {
return (
<div className={cx(styles.checkmark, fadeOut && styles.fadeOut)}>
<div className={cx(styles.circle, secondary && styles.secondary)} />
<div className={cx(styles.stem, secondary && styles.secondary)} />
<div className={cx(styles.kick, secondary && styles.secondary)} />
</div>
<svg
id="icon-checkmark"
xmlns="http://www.w3.org/2000/svg"
viewBox="4 4 40 40"
width={size}
height={size}
className={cx(
styles.checkmark,
secondary && styles.secondary,
fadeOut && styles.fadeOut,
disabled && styles.disabled
)}
>
<path d="M21.05 33.1 35.2 18.95l-2.3-2.25-11.85 11.85-6-6-2.25 2.25ZM24 44q-4.1 0-7.75-1.575-3.65-1.575-6.375-4.3-2.725-2.725-4.3-6.375Q4 28.1 4 24q0-4.15 1.575-7.8 1.575-3.65 4.3-6.35 2.725-2.7 6.375-4.275Q19.9 4 24 4q4.15 0 7.8 1.575 3.65 1.575 6.35 4.275 2.7 2.7 4.275 6.35Q44 19.85 44 24q0 4.1-1.575 7.75-1.575 3.65-4.275 6.375t-6.35 4.3Q28.15 44 24 44Zm0-3q7.1 0 12.05-4.975Q41 31.05 41 24q0-7.1-4.95-12.05Q31.1 7 24 7q-7.05 0-12.025 4.95Q7 16.9 7 24q0 7.05 4.975 12.025Q16.95 41 24 41Zm0-17Z" />
</svg>
);
};

View File

@ -1,14 +1,24 @@
import * as preact from "preact";
import styles from "./styles.sass";
import { IconProps } from "./Icon";
import cx from "classnames";
const ExclamationMark = () => {
const ExclamationMark = ({ size, secondary, disabled }: IconProps) => {
return (
<div className={styles.exclamationMark}>
<div className={styles.circle} />
<div className={styles.stem} />
<div className={styles.dot} />
</div>
<svg
id="icon-exclamation"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={size}
height={size}
className={cx(
styles.exclamationMark,
secondary && styles.secondary,
disabled && styles.disabled
)}
>
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/>
</svg>
);
};

View File

@ -0,0 +1,26 @@
import * as preact from "preact";
import { IconProps } from "./Icon";
import cx from "classnames";
import styles from "./styles.sass";
const GitHub = ({ size, secondary, disabled }: IconProps) => {
return (
<svg
id="icon-github"
xmlns="http://www.w3.org/2000/svg"
fill="#fff"
viewBox="0 0 97.63 96"
width={size}
height={size}
className={cx(
styles.icon,
secondary && styles.secondary,
disabled && styles.disabled
)}
>
<path d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" />{" "}
</svg>
);
};
export default GitHub;

View File

@ -0,0 +1,49 @@
import * as preact from "preact";
import styles from "./styles.sass";
import { IconProps } from "./Icon";
import cx from "classnames";
const Google = ({ size, disabled }: IconProps) => {
return (
<svg
id="icon-google"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={size}
height={size}
className={styles.googleIcon}
>
<path
className={cx(
styles.googleIcon,
disabled ? styles.disabled : styles.blue
)}
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
/>
<path
className={cx(
styles.googleIcon,
disabled ? styles.disabled : styles.green
)}
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
/>
<path
className={cx(
styles.googleIcon,
disabled ? styles.disabled : styles.yellow
)}
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
/>
<path
className={cx(
styles.googleIcon,
disabled ? styles.disabled : styles.red
)}
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
/>
<path d="M1 1h22v22H1z" fill="none" />
</svg>
);
};
export default Google;

View File

@ -0,0 +1,36 @@
import * as preact from "preact";
import * as icons from "./icons";
export type IconName = keyof typeof icons;
export type IconProps = {
secondary?: boolean;
fadeOut?: boolean;
disabled?: boolean;
size?: number;
};
type Props = IconProps & {
name: IconName;
};
const Icon = ({
name,
secondary,
size = 18,
fadeOut,
disabled,
}: Props) => {
const Ico = icons[name];
return (
<Ico
size={size}
secondary={secondary}
fadeOut={fadeOut}
disabled={disabled}
/>
);
};
export default Icon;

View File

@ -1,11 +1,7 @@
import * as preact from "preact";
import { ComponentChildren } from "preact";
import cx from "classnames";
import Checkmark from "./Checkmark";
import { ComponentChildren, Fragment } from "preact";
import styles from "./styles.sass";
import Icon from "./Icon";
export type Props = {
children?: ComponentChildren;
@ -13,6 +9,7 @@ export type Props = {
isSuccess?: boolean;
fadeOut?: boolean;
secondary?: boolean;
hasIcon?: boolean;
};
const LoadingSpinner = ({
@ -21,19 +18,30 @@ const LoadingSpinner = ({
isSuccess,
fadeOut,
secondary,
hasIcon,
}: Props) => {
return (
<div className={styles.loadingSpinnerWrapper}>
<Fragment>
{isLoading ? (
<div
className={cx(styles.loadingSpinner, secondary && styles.secondary)}
/>
<div className={styles.loadingSpinnerWrapper}>
<Icon name={"spinner"} secondary={secondary} />
</div>
) : isSuccess ? (
<Checkmark fadeOut={fadeOut} secondary={secondary} />
<div className={styles.loadingSpinnerWrapper}>
<Icon name={"checkmark"} secondary={secondary} fadeOut={fadeOut} />
</div>
) : (
children
<div
className={
hasIcon
? styles.loadingSpinnerWrapperIcon
: styles.loadingSpinnerWrapper
}
>
{children}
</div>
)}
</div>
</Fragment>
);
};

View File

@ -0,0 +1,35 @@
import * as preact from "preact";
import { IconProps } from "./Icon";
import styles from "./styles.sass";
import cx from "classnames";
const Passkey = ({ size, secondary, disabled }: IconProps) => {
return (
<svg
id="icon-passkey"
xmlns="http://www.w3.org/2000/svg"
viewBox="3 1.5 19.5 19"
width={size}
height={size}
className={cx(
styles.icon,
secondary && styles.secondary,
disabled && styles.disabled
)}
>
<g id="icon-passkey-all">
<circle id="icon-passkey-head" cx="10.5" cy="6" r="4.5" />
<path
id="icon-passkey-key"
d="M22.5,10.5a3.5,3.5,0,1,0-5,3.15V19L19,20.5,21.5,18,20,16.5,21.5,15l-1.24-1.24A3.5,3.5,0,0,0,22.5,10.5Zm-3.5,0a1,1,0,1,1,1-1A1,1,0,0,1,19,10.5Z"
/>
<path
id="icon-passkey-body"
d="M14.44,12.52A6,6,0,0,0,12,12H9a6,6,0,0,0-6,6v2H16V14.49A5.16,5.16,0,0,1,14.44,12.52Z"
/>
</g>
</svg>
);
};
export default Passkey;

View File

@ -0,0 +1,25 @@
import * as preact from "preact";
import { IconProps } from "./Icon";
import styles from "./styles.sass";
import cx from "classnames";
const Spinner = ({ size, disabled }: IconProps) => {
return (
<svg
id="icon-spinner"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={size}
height={size}
className={cx(styles.loadingSpinner, disabled && styles.disabled)}
>
<path
d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
opacity=".25"
/>
<path d="M10.72,19.9a8,8,0,0,1-6.5-9.79A7.77,7.77,0,0,1,10.4,4.16a8,8,0,0,1,9.49,6.52A1.54,1.54,0,0,0,21.38,12h.13a1.37,1.37,0,0,0,1.38-1.54,11,11,0,1,0-12.7,12.39A1.54,1.54,0,0,0,12,21.34h0A1.47,1.47,0,0,0,10.72,19.9Z" />
</svg>
);
};
export default Spinner;

View File

@ -0,0 +1,8 @@
import { default as passkey } from "./Passkey";
import { default as spinner } from "./Spinner";
import { default as checkmark } from "./Checkmark";
import { default as exclamation } from "./ExclamationMark";
import { default as google } from "./Google";
import { default as github } from "./GitHub";
export { passkey, spinner, checkmark, exclamation, google, github };

View File

@ -1,50 +1,24 @@
@use '../../variables'
.icon
display: inline-block
fill: variables.$brand-contrast-color
width: 18px
&.secondary
fill: variables.$color
&.disabled
fill: variables.$color-shade-1
// Checkmark Styles
.checkmark
display: inline-block
width: 16px
height: 16px
transform: rotate(45deg)
@extend .icon
fill: variables.$brand-color
.circle
box-sizing: border-box
display: inline-block
border-width: 2px
border-style: solid
border-color: variables.$brand-color
position: absolute
width: 16px
height: 16px
border-radius: 11px
left: 0
top: 0
&.secondary
border-color: variables.$color-shade-1
.stem
position: absolute
width: 2px
height: 7px
background-color: variables.$brand-color
left: 8px
top: 3px
&.secondary
background-color: variables.$color-shade-1
.kick
position: absolute
width: 5px
height: 2px
background-color: variables.$brand-color
left: 5px
top: 10px
&.secondary
background-color: variables.$color-shade-1
&.secondary
fill: variables.$color-shade-1
&.fadeOut
animation: fadeOut ease-out 1.5s forwards !important
@ -59,59 +33,32 @@
// ExclamationMark Styles
.exclamationMark
width: 16px
height: 16px
position: relative
margin: 5px
.circle
box-sizing: border-box
display: inline-block
background-color: variables.$error-color
position: absolute
width: 16px
height: 16px
border-radius: 11px
left: 0
top: 0
.stem
position: absolute
width: 2px
height: 6px
background: variables.$background-color
left: 7px
top: 3px
.dot
position: absolute
width: 2px
height: 2px
background: variables.$background-color
left: 7px
top: 10px
@extend .icon
fill: variables.$error-color
padding-right: 5px
// Loading Spinner Styles
.loadingSpinnerWrapperIcon
@extend .loadingSpinnerWrapper
justify-content: flex-start
width: 100%
column-gap: 10px
margin-left: 10px
.loadingSpinnerWrapper
display: inline-block
display: inline-flex
align-items: center
height: 100%
margin: 0 5px
.loadingSpinner
box-sizing: border-box
display: inline-block
border-width: 2px
border-style: solid
border-color: variables.$background-color
border-top: 2px solid variables.$brand-color
border-radius: 50%
width: 16px
height: 16px
@extend .icon
fill: variables.$brand-color
animation: spin 500ms ease-in-out infinite
&.secondary
border-color: variables.$color-shade-1
border-top: 2px solid variables.$color-shade-2
&.secondary
fill: variables.$color-shade-1
@keyframes spin
0%
@ -119,3 +66,18 @@
100%
transform: rotate(360deg)
// Google Styles
.googleIcon
&.disabled
fill: variables.$color-shade-1
&.blue
fill: #4285F4
&.green
fill: #34A853
&.yellow
fill: #FBBC05
&.red
fill: #EA4335

View File

@ -77,7 +77,7 @@ const AppProvider = ({
const ref = useRef<HTMLElement>(null);
const hanko = useMemo(() => {
if (api.length) {
if (api) {
return new Hanko(api, 13000);
}
return null;

View File

@ -31,7 +31,7 @@ const InitPage = () => {
.then((shouldRegister) =>
shouldRegister ? <RegisterPasskeyPage /> : <LoginFinishedPage />
),
[hanko.webauthn]
[hanko]
);
const initHankoAuth = useCallback(() => {
@ -56,7 +56,7 @@ const InitPage = () => {
}
return <LoginEmailPage />;
});
}, [afterLogin, hanko.config, hanko.user, setConfig, setUser]);
}, [afterLogin, hanko, setConfig, setUser]);
const initHankoProfile = useCallback(
() =>
@ -81,13 +81,14 @@ const InitPage = () => {
}, [componentName, initHankoAuth, initHankoProfile]);
useEffect(() => {
if (!hanko) return;
const initializer = getInitializer();
if (initializer) {
initializer()
.then(setPage)
.catch((e) => setPage(<ErrorPage initialError={e} />));
}
}, [getInitializer, setPage]);
}, [hanko, getInitializer, setPage]);
return <LoadingSpinner isLoading />;
};

View File

@ -30,6 +30,7 @@ import Form from "../components/form/Form";
import Divider from "../components/divider/Divider";
import ErrorMessage from "../components/error/ErrorMessage";
import Headline1 from "../components/headline/Headline1";
import { IconName } from "../components/icons/Icon";
import LoginPasscodePage from "./LoginPasscodePage";
import RegisterConfirmPage from "./RegisterConfirmPage";
@ -405,6 +406,7 @@ const LoginEmailPage = (props: Props) => {
isLoading={isPasskeyLoginLoading}
isSuccess={isPasskeyLoginSuccess}
disabled={disabled}
icon={"passkey"}
>
{t("labels.signInPasskey")}
</Button>
@ -421,6 +423,7 @@ const LoginEmailPage = (props: Props) => {
secondary
isLoading={isThirdPartyLoginLoading === provider}
disabled={disabled}
icon={provider.toLowerCase() as IconName}
>
{t("labels.signInWith", {
provider,

View File

@ -82,6 +82,7 @@ const RegisterPasskeyPage = () => {
isSuccess={isSuccess}
isLoading={isPasskeyLoading}
disabled={disabled}
icon={"passkey"}
>
{t("labels.registerAuthenticator")}
</Button>