0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-01-06 20:40:08 -05:00

feat(console): toggle switch

This commit is contained in:
wangsijie 2022-04-13 15:10:40 +08:00
parent a71e0dfdb6
commit 717b79b9f6
No known key found for this signature in database
GPG key ID: C72642FE24F7D42B
5 changed files with 119 additions and 39 deletions

View file

@ -0,0 +1,61 @@
@use '@/scss/underscore' as _;
.switch {
position: relative;
display: inline-block;
width: 40px;
height: 24px;
input {
opacity: 0%;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: var(--color-neutral-90);
transition: 0.4s;
border-radius: 12px;
&::before {
position: absolute;
content: '';
height: 20px;
width: 20px;
left: 2px;
bottom: 2px;
background-color: #fff;
transition: 0.4s;
border-radius: 50%;
box-shadow: 0 3px 7px rgba(0, 0, 0, 12%);
}
}
input:checked + .slider {
background-color: var(--color-success-70);
}
input:checked + .slider::before {
transform: translateX(16px);
}
}
.wrapper {
display: flex;
align-items: center;
border: 1px solid var(--color-neutral-90);
border-radius: _.unit(2);
padding: _.unit(4);
.label {
flex: 1;
margin-right: _.unit(2);
font: var(--font-body-medium);
}
}

View file

@ -0,0 +1,21 @@
import React, { forwardRef, HTMLProps, ReactNode, Ref } from 'react';
import * as styles from './index.module.scss';
type Props = HTMLProps<HTMLInputElement> & {
label?: ReactNode;
};
const Switch = ({ label, ...rest }: Props, ref?: Ref<HTMLInputElement>) => {
return (
<div className={styles.wrapper}>
<div className={styles.label}>{label}</div>
<label className={styles.switch}>
<input type="checkbox" {...rest} ref={ref} />
<span className={styles.slider} />
</label>
</div>
);
};
export default forwardRef<HTMLInputElement, Props>(Switch);

View file

@ -1,22 +1,18 @@
import { BrandingStyle, SignInExperience } from '@logto/schemas';
import React from 'react';
import { Control, Controller, UseFormRegister, UseFormWatch } from 'react-hook-form';
import { Controller, useFormContext } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import FormField from '@/components/FormField';
import RadioGroup, { Radio } from '@/components/RadioGroup';
import Switch from '@/components/Switch';
import TextInput from '@/components/TextInput';
import * as styles from './index.module.scss';
type Props = {
register: UseFormRegister<SignInExperience>;
control: Control<SignInExperience>;
watch: UseFormWatch<SignInExperience>;
};
const BrandingForm = ({ register, control, watch }: Props) => {
const BrandingForm = () => {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const { watch, register, control } = useFormContext<SignInExperience>();
const isDarkModeEnabled = watch('branding.isDarkModeEnabled');
const style = watch('branding.style');
@ -30,9 +26,10 @@ const BrandingForm = ({ register, control, watch }: Props) => {
<TextInput {...register('branding.primaryColor', { required: true })} />
</FormField>
<FormField isRequired title="admin_console.sign_in_exp.branding.dark_mode">
{/* TODO: LOG-2152 switch */}
<TextInput {...register('branding.isDarkModeEnabled', { required: true })} />
<div>{t('sign_in_exp.branding.dark_mode_description')}</div>
<Switch
label={t('sign_in_exp.branding.dark_mode_description')}
{...register('branding.isDarkModeEnabled', { required: true })}
/>
</FormField>
<FormField
isRequired={isDarkModeEnabled}

View file

@ -1,28 +1,27 @@
import { SignInExperience } from '@logto/schemas';
import React from 'react';
import { UseFormRegister, UseFormWatch } from 'react-hook-form';
import { useFormContext } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import FormField from '@/components/FormField';
import Switch from '@/components/Switch';
import TextInput from '@/components/TextInput';
import * as styles from './index.module.scss';
type Props = {
register: UseFormRegister<SignInExperience>;
watch: UseFormWatch<SignInExperience>;
};
const TermsForm = ({ register, watch }: Props) => {
const TermsForm = () => {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const { watch, register } = useFormContext<SignInExperience>();
const enabled = watch('termsOfUse.enabled');
return (
<>
<div className={styles.title}>{t('sign_in_exp.terms_of_use.title')}</div>
<FormField isRequired title="admin_console.sign_in_exp.terms_of_use.enable">
<TextInput {...register('termsOfUse.enabled', { required: true })} />
<div>{t('sign_in_exp.terms_of_use.description')}</div>
<Switch
{...register('termsOfUse.enabled', { required: true })}
label={t('sign_in_exp.terms_of_use.description')}
/>
</FormField>
<FormField isRequired={enabled} title="admin_console.sign_in_exp.terms_of_use.terms_of_use">
<TextInput {...register('termsOfUse.contentUrl', { required: enabled })} />

View file

@ -1,6 +1,6 @@
import { SignInExperience as SignInExperienceType } from '@logto/schemas';
import React, { useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { FormProvider, useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
@ -21,14 +21,12 @@ const SignInExperience = () => {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const { tab } = useParams();
const { data, error, mutate } = useSWR<SignInExperienceType, RequestError>('/api/sign-in-exp');
const methods = useForm<SignInExperienceType>();
const {
reset,
control,
handleSubmit,
register,
watch,
formState: { isSubmitting },
} = useForm<SignInExperienceType>();
} = methods;
const api = useApi();
useEffect(() => {
@ -72,20 +70,24 @@ const SignInExperience = () => {
{!data && !error && <div>loading</div>}
{error && <div>{`error occurred: ${error.body.message}`}</div>}
{data && (
<form onSubmit={onSubmit}>
{tab === 'experience' && (
<BrandingForm register={register} control={control} watch={watch} />
)}
{tab === 'experience' && <TermsForm register={register} watch={watch} />}
<div className={detailsStyles.footer}>
<Button
disabled={isSubmitting}
type="primary"
htmlType="submit"
title="general.save_changes"
/>
</div>
</form>
<FormProvider {...methods}>
<form onSubmit={onSubmit}>
{tab === 'experience' && (
<>
<BrandingForm />
<TermsForm />
</>
)}
<div className={detailsStyles.footer}>
<Button
disabled={isSubmitting}
type="primary"
htmlType="submit"
title="general.save_changes"
/>
</div>
</form>
</FormProvider>
)}
</Card>
</div>