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:
parent
a71e0dfdb6
commit
717b79b9f6
5 changed files with 119 additions and 39 deletions
61
packages/console/src/components/Switch/index.module.scss
Normal file
61
packages/console/src/components/Switch/index.module.scss
Normal 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);
|
||||
}
|
||||
}
|
21
packages/console/src/components/Switch/index.tsx
Normal file
21
packages/console/src/components/Switch/index.tsx
Normal 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);
|
|
@ -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}
|
||||
|
|
|
@ -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 })} />
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue