@devseed-ui/form
Version:
devseed UI Kit Form
203 lines (180 loc) • 4.95 kB
JavaScript
import React from 'react';
import T from 'prop-types';
import styled, { css } from 'styled-components';
import { CollecticonTickSmall, iconDataURI } from '@devseed-ui/collecticons';
import {
disabled,
rgba,
themeVal,
visuallyHidden
} from '@devseed-ui/theme-provider';
/**
* Renders a FormCheckable component.
*
* @param {string} type Determines the type of input to render. Can be one of
* `checkbox` or `radio`
* @param {string} name (html prop) name to be used as `name` prop of
* the element
* @param {string} id (html prop) id to be used as `id` prop of the element
* @param {string} title (html prop) Label's title attribute
* @param {boolean} checked Whether or not the FormCheckable is checked
* @param {func} onChange Change callback for the FormCheckable
* @param {string} value Value for the underlying input element
* @param {node} children Textual content
* @param {string} textPlacement Where to position the text. `left` or `right`
* of the control. Default to `right`
*/
const FormCheckableElement = (props) => {
const {
children,
id,
name,
title,
type,
checked,
onChange,
value,
className,
textPlacement,
hideText,
...rest
} = props;
return (
<label htmlFor={id} className={className} title={title} {...rest}>
<input
type={type}
name={name}
id={id}
value={value}
checked={checked}
onChange={onChange}
/>
{textPlacement === 'right' && <FormCheckableControl />}
<FormCheckableText hideText={hideText}>{children}</FormCheckableText>
{textPlacement === 'left' && <FormCheckableControl />}
</label>
);
};
FormCheckableElement.propTypes = {
name: T.string.isRequired,
id: T.string.isRequired,
textPlacement: T.oneOf(['right', 'left']),
hideText: T.bool,
className: T.string,
title: T.string,
type: T.oneOf(['checkbox', 'radio']),
checked: T.bool,
children: T.node.isRequired,
onChange: T.func,
value: T.oneOfType([T.number, T.string])
};
FormCheckableElement.defaultProps = {
textPlacement: 'right'
};
const formCheckableWrapper = css`
display: inline-grid;
gap: 0.5rem;
font-size: 1rem;
line-height: 1.5;
cursor: pointer;
justify-content: start;
> * {
grid-row: 1;
}
input {
flex: none;
}
`;
const FormCheckableText = styled.span`
line-height: 1.5;
/* Hide Text */
${({ hideText }) => hideText && visuallyHidden()}
`;
const FormCheckableControl = styled.span`
position: relative;
transition: all 0.16s ease 0s;
`;
export const FormCheckable = styled(FormCheckableElement)`
${formCheckableWrapper}
input {
${visuallyHidden()}
}
${FormCheckableControl} {
display: flex;
align-items: center;
justify-content: center;
margin: 0.125rem 0;
height: 1.25rem;
width: 1.25rem;
background: ${themeVal('color.surface')};
line-height: 1;
border: ${themeVal('layout.border')} solid
${rgba(themeVal('color.base'), 0.16)};
border-radius: ${({ type }) =>
type === 'checkbox'
? themeVal('shape.rounded')
: themeVal('shape.ellipsoid')};
&::before {
transition: all 0.24s ease 0s;
opacity: 0;
content: '';
${({ type, theme }) =>
type === 'checkbox'
? css`
background-image: url('${iconDataURI(CollecticonTickSmall, {
color: theme.color.base
})}');
background-size: 1rem 1rem;
height: 1rem;
width: 1rem;
background-repeat: no-repeat;
`
: css`
height: 0.5rem;
width: 0.5rem;
background: ${themeVal('color.base')};
border-radius: ${themeVal('shape.ellipsoid')};
`}
}
}
&:hover ${FormCheckableControl} {
border-width: ${themeVal('layout.border')};
border-color: ${rgba(themeVal('color.base'), 0.32)};
}
${({ invalid }) =>
invalid &&
css`
${FormCheckableControl} {
border-width: ${themeVal('layout.border')};
border-color: ${themeVal('color.danger')};
}
&:hover
${FormCheckableControl},
&:focus
${FormCheckableControl},
&:active
${FormCheckableControl} {
border-color: ${themeVal('color.danger')};
}
`}
input:focus ~ ${FormCheckableControl},
input:active ~ ${FormCheckableControl} {
outline: 0;
border-width: ${themeVal('layout.border')};
border-color: ${rgba(themeVal('color.base'), 0.64)};
}
${({ checked }) => (checked ? `${FormCheckableControl},` : '')}
input:checked ~ ${FormCheckableControl} {
&::before {
opacity: 1;
}
}
&[disabled] {
${disabled()}
}
`;
export const FormCheckableGroup = styled.div`
display: flex;
flex-flow: row wrap;
gap: 0.5rem 1rem;
`;