@furystack/shades-common-components
Version:
Common UI components for FuryStack Shades
176 lines • 6.97 kB
JavaScript
import { Shade, createComponent } from '@furystack/shades';
import { cssVariableTheme } from '../../services/css-variable-theme.js';
import { ThemeProviderService } from '../../services/theme-provider-service.js';
import { FormContextToken } from '../form.js';
export const Checkbox = Shade({
customElementName: 'shade-checkbox',
css: {
display: 'inline-flex',
fontFamily: cssVariableTheme.typography.fontFamily,
alignItems: 'center',
'& label': {
display: 'inline-flex',
alignItems: 'center',
gap: cssVariableTheme.spacing.sm,
cursor: 'pointer',
fontSize: cssVariableTheme.typography.fontSize.sm,
color: cssVariableTheme.text.primary,
userSelect: 'none',
webkitUserSelect: 'none',
},
'& .checkbox-control': {
position: 'relative',
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
width: '20px',
height: '20px',
flexShrink: '0',
},
'& input[type="checkbox"]': {
appearance: 'none',
webkitAppearance: 'none',
width: '20px',
height: '20px',
margin: '0',
border: `2px solid ${cssVariableTheme.text.secondary}`,
borderRadius: cssVariableTheme.shape.borderRadius.xs,
backgroundColor: 'transparent',
cursor: 'pointer',
transition: `all ${cssVariableTheme.transitions.duration.fast} ${cssVariableTheme.transitions.easing.default}`,
outline: 'none',
},
'& input[type="checkbox"]:hover:not(:disabled)': {
borderColor: 'var(--checkbox-color)',
},
'& input[type="checkbox"]:focus-visible': {
outline: 'none',
boxShadow: cssVariableTheme.action.focusRing,
},
'& input[type="checkbox"]:checked': {
backgroundColor: 'var(--checkbox-color)',
borderColor: 'var(--checkbox-color)',
},
'& input[type="checkbox"]:checked::after': {
content: '""',
position: 'absolute',
left: '6px',
top: '2px',
width: '5px',
height: '10px',
border: `solid ${cssVariableTheme.background.paper}`,
borderWidth: '0 2px 2px 0',
transform: 'rotate(45deg)',
pointerEvents: 'none',
},
'&[data-indeterminate] input[type="checkbox"]': {
backgroundColor: 'var(--checkbox-color)',
borderColor: 'var(--checkbox-color)',
},
'&[data-indeterminate] input[type="checkbox"]::after': {
content: '""',
position: 'absolute',
left: '4px',
top: '8px',
width: '10px',
height: '2px',
background: cssVariableTheme.background.paper,
border: 'none',
transform: 'none',
pointerEvents: 'none',
},
'&[data-disabled] label': {
color: cssVariableTheme.text.disabled,
cursor: 'not-allowed',
},
'&[data-disabled] input[type="checkbox"]': {
opacity: cssVariableTheme.action.disabledOpacity,
cursor: 'not-allowed',
},
// Size: small
'&[data-size="small"] label': {
fontSize: cssVariableTheme.typography.fontSize.xs,
},
'&[data-size="small"] .checkbox-control': {
width: '16px',
height: '16px',
},
'&[data-size="small"] input[type="checkbox"]': {
width: '16px',
height: '16px',
},
'&[data-size="small"] input[type="checkbox"]:checked::after': {
left: '4px',
top: '1px',
width: '4px',
height: '8px',
},
'&[data-size="small"][data-indeterminate] input[type="checkbox"]::after': {
left: '3px',
top: '6px',
width: '8px',
},
// Size: large
'&[data-size="large"] label': {
fontSize: cssVariableTheme.typography.fontSize.md,
},
'&[data-size="large"] .checkbox-control': {
width: '24px',
height: '24px',
},
'&[data-size="large"] input[type="checkbox"]': {
width: '24px',
height: '24px',
},
'&[data-size="large"] input[type="checkbox"]:checked::after': {
left: '8px',
top: '3px',
width: '6px',
height: '12px',
},
'&[data-size="large"][data-indeterminate] input[type="checkbox"]::after': {
left: '5px',
top: '10px',
width: '12px',
},
},
render: ({ props, injector, useDisposable, useHostProps, useRef }) => {
const inputRef = useRef('formInput');
useDisposable('form-registration', () => {
const formService = injector.get(FormContextToken);
if (formService) {
queueMicrotask(() => {
if (inputRef.current)
formService.inputs.add(inputRef.current);
});
}
return {
[Symbol.dispose]: () => {
if (inputRef.current && formService)
formService.inputs.delete(inputRef.current);
},
};
});
const themeProvider = injector.get(ThemeProviderService);
const color = themeProvider.theme.palette[props.color || 'primary'].main;
useHostProps({
'data-size': props.size && props.size !== 'medium' ? props.size : undefined,
'data-disabled': props.disabled ? '' : undefined,
'data-indeterminate': props.indeterminate ? '' : undefined,
style: { '--checkbox-color': color },
});
// The host element already receives `props.onchange` via Shade's attachProps;
// a bubbled change from the inner input triggers it once. This handler exists
// only to keep the input in its `indeterminate` visual state across toggles.
const handleChange = () => {
if (props.indeterminate && inputRef.current) {
inputRef.current.indeterminate = true;
}
};
return (createComponent("label", { ...props.labelProps },
createComponent("span", { className: "checkbox-control" },
createComponent("input", { ref: inputRef, type: "checkbox", checked: props.checked, disabled: props.disabled, name: props.name, required: props.required, onchange: handleChange, ...(props.value !== undefined ? { value: props.value } : {}) })),
props.labelTitle ? createComponent("span", { className: "checkbox-label" }, props.labelTitle) : null));
},
});
//# sourceMappingURL=checkbox.js.map