bigbuffet-rw/app/soapbox/features/forms/index.tsx

185 lines
4.3 KiB
TypeScript
Raw Normal View History

import clsx from 'clsx';
2022-12-17 12:53:02 -08:00
import React, { useState } from 'react';
2022-04-28 10:43:36 -07:00
import { v4 as uuidv4 } from 'uuid';
2022-12-17 12:53:02 -08:00
import { Select } from '../../components/ui';
2022-04-28 10:43:36 -07:00
interface IInputContainer {
label?: React.ReactNode
hint?: React.ReactNode
required?: boolean
type?: string
extraClass?: string
error?: boolean
children: React.ReactNode
2022-04-28 10:43:36 -07:00
}
export const InputContainer: React.FC<IInputContainer> = (props) => {
const containerClass = clsx('input', {
2022-04-28 10:43:36 -07:00
'with_label': props.label,
'required': props.required,
'boolean': props.type === 'checkbox',
'field_with_errors': props.error,
}, props.extraClass);
return (
<div className={containerClass}>
{props.children}
{props.hint && <span className='hint'>{props.hint}</span>}
</div>
);
};
interface ILabelInputContainer {
label?: React.ReactNode
hint?: React.ReactNode
children: React.ReactNode
2022-04-28 10:43:36 -07:00
}
export const LabelInputContainer: React.FC<ILabelInputContainer> = ({ label, hint, children }) => {
const [id] = useState(uuidv4());
const childrenWithProps = React.Children.map(children, child => (
// @ts-ignore: not sure how to get the right type here
React.cloneElement(child, { id: id, key: id })
));
return (
<div className='label_input'>
<label htmlFor={id}>{label}</label>
<div className='label_input__wrapper'>
{childrenWithProps}
</div>
{hint && <span className='hint'>{hint}</span>}
</div>
);
};
interface ILabelInput {
label?: React.ReactNode
2022-04-28 10:43:36 -07:00
}
export const LabelInput: React.FC<ILabelInput> = ({ label, ...props }) => (
<LabelInputContainer label={label}>
<input {...props} />
</LabelInputContainer>
);
interface ILabelTextarea {
label?: React.ReactNode
2022-04-28 10:43:36 -07:00
}
export const LabelTextarea: React.FC<ILabelTextarea> = ({ label, ...props }) => (
<LabelInputContainer label={label}>
<textarea {...props} />
</LabelInputContainer>
);
interface ISimpleInput {
type: string
label?: React.ReactNode
hint?: React.ReactNode
error?: boolean
onChange?: React.ChangeEventHandler
min?: number
max?: number
pattern?: string
name?: string
placeholder?: string
value?: string | number
autoComplete?: string
autoCorrect?: string
autoCapitalize?: string
required?: boolean
2022-04-28 10:43:36 -07:00
}
export const SimpleInput: React.FC<ISimpleInput> = (props) => {
2022-04-29 14:47:24 -07:00
const { hint, error, ...rest } = props;
const Input = props.label ? LabelInput : 'input';
2022-04-28 10:43:36 -07:00
return (
<InputContainer {...props}>
<Input {...rest} />
</InputContainer>
);
};
interface ICheckbox {
label?: React.ReactNode
hint?: React.ReactNode
name?: string
checked?: boolean
disabled?: boolean
onChange?: React.ChangeEventHandler<HTMLInputElement>
required?: boolean
}
export const Checkbox: React.FC<ICheckbox> = (props) => (
2022-04-28 10:43:36 -07:00
<SimpleInput type='checkbox' {...props} />
);
interface ISelectDropdown {
label?: React.ReactNode
hint?: React.ReactNode
items: Record<string, string>
defaultValue?: string
onChange?: React.ChangeEventHandler
2022-04-28 10:43:36 -07:00
}
export const SelectDropdown: React.FC<ISelectDropdown> = (props) => {
const { label, hint, items, ...rest } = props;
const optionElems = Object.keys(items).map(item => (
<option key={item} value={item}>{items[item]}</option>
));
// @ts-ignore
const selectElem = <Select {...rest}>{optionElems}</Select>;
return label ? (
<LabelInputContainer label={label} hint={hint}>{selectElem}</LabelInputContainer>
) : selectElem;
};
2022-04-28 15:27:12 -07:00
interface ITextInput {
name?: string
onChange?: React.ChangeEventHandler
label?: React.ReactNode
hint?: React.ReactNode
placeholder?: string
value?: string
autoComplete?: string
autoCorrect?: string
autoCapitalize?: string
pattern?: string
error?: boolean
required?: boolean
2022-04-28 15:27:12 -07:00
}
export const TextInput: React.FC<ITextInput> = props => (
2022-04-28 10:43:36 -07:00
<SimpleInput type='text' {...props} />
);
export const FileChooser : React.FC = (props) => (
<SimpleInput type='file' {...props} />
);
FileChooser.defaultProps = {
accept: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
};
2022-05-05 12:25:57 -07:00
interface IFileChooserLogo {
label?: React.ReactNode
hint?: React.ReactNode
name?: string
accept?: string[]
onChange: React.ChangeEventHandler<HTMLInputElement>
2022-05-05 12:25:57 -07:00
}
export const FileChooserLogo: React.FC<IFileChooserLogo> = props => (
2022-04-28 10:43:36 -07:00
<SimpleInput type='file' {...props} />
);
FileChooserLogo.defaultProps = {
accept: ['image/svg', 'image/png'],
};