chore: abstracts CheckboxInput into a uncontrolled input
This commit is contained in:
62
src/admin/components/forms/field-types/Checkbox/Input.tsx
Normal file
62
src/admin/components/forms/field-types/Checkbox/Input.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import React from 'react';
|
||||
import Check from '../../../icons/Check';
|
||||
import Label from '../../Label';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const baseClass = 'custom-checkbox';
|
||||
|
||||
type CheckboxInputProps = {
|
||||
onToggle: React.MouseEventHandler<HTMLButtonElement>
|
||||
inputRef?: React.MutableRefObject<HTMLInputElement>
|
||||
readOnly?: boolean
|
||||
checked?: boolean
|
||||
name?: string
|
||||
id?: string
|
||||
label?: string
|
||||
required?: boolean
|
||||
}
|
||||
export const CheckboxInput: React.FC<CheckboxInputProps> = (props) => {
|
||||
const {
|
||||
onToggle,
|
||||
checked,
|
||||
inputRef,
|
||||
name,
|
||||
id,
|
||||
label,
|
||||
readOnly,
|
||||
required,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<span
|
||||
className={[
|
||||
baseClass,
|
||||
checked && `${baseClass}--checked`,
|
||||
readOnly && `${baseClass}--read-only`,
|
||||
].filter(Boolean).join(' ')}
|
||||
>
|
||||
<input
|
||||
ref={inputRef}
|
||||
id={id}
|
||||
type="checkbox"
|
||||
name={name}
|
||||
checked={checked}
|
||||
readOnly
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onToggle}
|
||||
>
|
||||
<span className={`${baseClass}__input`}>
|
||||
<Check />
|
||||
</span>
|
||||
<Label
|
||||
htmlFor={id}
|
||||
label={label}
|
||||
required={required}
|
||||
/>
|
||||
</button>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
@@ -14,6 +14,46 @@
|
||||
&__error-wrap {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.custom-checkbox {
|
||||
label {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
input {
|
||||
// hidden HTML checkbox
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&__input {
|
||||
// visible checkbox
|
||||
@include formInput;
|
||||
padding: 0;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
width: $baseline;
|
||||
height: $baseline;
|
||||
margin-right: base(.5);
|
||||
|
||||
svg {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&--read-only {
|
||||
.custom-checkbox__input {
|
||||
background-color: var(--theme-elevation-100);
|
||||
}
|
||||
|
||||
label {
|
||||
color: var(--theme-elevation-400);
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
@extend %btn-reset;
|
||||
@@ -33,45 +73,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__input {
|
||||
@include formInput;
|
||||
padding: 0;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
width: $baseline;
|
||||
height: $baseline;
|
||||
margin-right: base(.5);
|
||||
|
||||
svg {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&--checked {
|
||||
button {
|
||||
.checkbox__input {
|
||||
.custom-checkbox__input {
|
||||
svg {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&--read-only {
|
||||
|
||||
.checkbox__label {
|
||||
color: var(--theme-elevation-400);
|
||||
}
|
||||
|
||||
.checkbox__input {
|
||||
background-color: var(--theme-elevation-100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@ import useField from '../../useField';
|
||||
import withCondition from '../../withCondition';
|
||||
import Error from '../../Error';
|
||||
import { checkbox } from '../../../../../fields/validations';
|
||||
import Check from '../../../icons/Check';
|
||||
import FieldDescription from '../../FieldDescription';
|
||||
import { Props } from './types';
|
||||
import { getTranslation } from '../../../../../utilities/getTranslation';
|
||||
import { CheckboxInput } from './Input';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
@@ -52,6 +52,15 @@ const Checkbox: React.FC<Props> = (props) => {
|
||||
condition,
|
||||
});
|
||||
|
||||
const onToggle = useCallback(() => {
|
||||
if (!readOnly) {
|
||||
setValue(!value);
|
||||
if (typeof onChange === 'function') onChange(!value);
|
||||
}
|
||||
}, [onChange, readOnly, setValue, value]);
|
||||
|
||||
const fieldID = `field-${path.replace(/\./gi, '__')}`;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={[
|
||||
@@ -73,27 +82,13 @@ const Checkbox: React.FC<Props> = (props) => {
|
||||
message={errorMessage}
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
id={`field-${path.replace(/\./gi, '__')}`}
|
||||
type="checkbox"
|
||||
<CheckboxInput
|
||||
onToggle={onToggle}
|
||||
id={fieldID}
|
||||
label={getTranslation(label || name, i18n)}
|
||||
name={path}
|
||||
checked={Boolean(value)}
|
||||
readOnly
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={readOnly ? undefined : () => {
|
||||
setValue(!value);
|
||||
if (typeof onChange === 'function') onChange(!value);
|
||||
}}
|
||||
>
|
||||
<span className={`${baseClass}__input`}>
|
||||
<Check />
|
||||
</span>
|
||||
<span className={`${baseClass}__label`}>
|
||||
{getTranslation(label || name, i18n)}
|
||||
</span>
|
||||
</button>
|
||||
<FieldDescription
|
||||
value={value}
|
||||
description={description}
|
||||
|
||||
Reference in New Issue
Block a user