@furystack/shades-common-components
Version:
Common UI components for FuryStack Shades
153 lines • 6.14 kB
JavaScript
import { Shade, createComponent } from '@furystack/shades';
import { buildTransition, cssVariableTheme } from '../../services/css-variable-theme.js';
import { ThemeProviderService } from '../../services/theme-provider-service.js';
import { FormContextToken } from '../form.js';
export const Switch = Shade({
customElementName: 'shade-switch',
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',
},
'& .switch-control': {
position: 'relative',
display: 'inline-flex',
alignItems: 'center',
flexShrink: '0',
},
// Medium size (default)
'& .switch-track': {
width: '40px',
height: '22px',
borderRadius: '11px',
backgroundColor: cssVariableTheme.text.secondary,
transition: buildTransition([
'background-color',
cssVariableTheme.transitions.duration.fast,
cssVariableTheme.transitions.easing.default,
]),
position: 'relative',
opacity: '0.5',
},
'& .switch-thumb': {
position: 'absolute',
top: '2px',
left: '2px',
width: '18px',
height: '18px',
borderRadius: '50%',
background: cssVariableTheme.background.paper,
transition: buildTransition(['transform', cssVariableTheme.transitions.duration.fast, cssVariableTheme.transitions.easing.default], ['box-shadow', cssVariableTheme.transitions.duration.fast, cssVariableTheme.transitions.easing.default]),
boxShadow: cssVariableTheme.shadows.sm,
pointerEvents: 'none',
},
// Small size
'&[data-size="small"] .switch-track': {
width: '32px',
height: '18px',
borderRadius: '9px',
},
'&[data-size="small"] .switch-thumb': {
width: '14px',
height: '14px',
},
// Large size
'&[data-size="large"] .switch-track': {
width: '48px',
height: '26px',
borderRadius: '13px',
},
'&[data-size="large"] .switch-thumb': {
width: '22px',
height: '22px',
},
// Hidden input
'& input[type="checkbox"]': {
position: 'absolute',
width: '1px',
height: '1px',
margin: '-1px',
padding: '0',
overflow: 'hidden',
clip: 'rect(0, 0, 0, 0)',
whiteSpace: 'nowrap',
border: '0',
},
// Checked state
'& input[type="checkbox"]:checked + .switch-track': {
backgroundColor: 'var(--switch-color)',
opacity: '1',
},
'& input[type="checkbox"]:checked + .switch-track .switch-thumb': {
transform: 'translateX(18px)',
},
'&[data-size="small"] input[type="checkbox"]:checked + .switch-track .switch-thumb': {
transform: 'translateX(14px)',
},
'&[data-size="large"] input[type="checkbox"]:checked + .switch-track .switch-thumb': {
transform: 'translateX(22px)',
},
// Hover state
'& .switch-control:hover .switch-track': {
opacity: '0.7',
},
'& .switch-control:hover input[type="checkbox"]:checked + .switch-track': {
opacity: '0.85',
},
// Focus state
'& input[type="checkbox"]:focus-visible + .switch-track': {
outline: 'none',
boxShadow: cssVariableTheme.action.focusRing,
},
// Disabled state
'&[data-disabled] label': {
color: cssVariableTheme.text.disabled,
cursor: 'not-allowed',
},
'&[data-disabled] .switch-control': {
opacity: cssVariableTheme.action.disabledOpacity,
pointerEvents: 'none',
},
},
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-disabled': props.disabled ? '' : undefined,
'data-size': props.size && props.size !== 'medium' ? props.size : undefined,
style: { '--switch-color': color },
});
return (createComponent("label", { ...props.labelProps },
createComponent("span", { className: "switch-control" },
createComponent("input", { ref: inputRef, type: "checkbox", role: "switch", checked: props.checked, disabled: props.disabled, name: props.name, required: props.required, ...(props.value !== undefined ? { value: props.value } : {}) }),
createComponent("span", { className: "switch-track" },
createComponent("span", { className: "switch-thumb" }))),
props.labelTitle ? createComponent("span", { className: "switch-label" }, props.labelTitle) : null));
},
});
//# sourceMappingURL=switch.js.map