UNPKG

@furystack/shades-common-components

Version:

176 lines 8.63 kB
import { Shade, attachStyles, createComponent } from '@furystack/shades'; import { ObservableValue } from '@furystack/utils'; import { ThemeProviderService } from '../../services/theme-provider-service.js'; import { FormService } from '../form.js'; const getLabelStyle = ({ themeProvider, props, state, validationResult, }) => { return { display: 'flex', flexDirection: 'column', alignItems: 'flex-start', justifyContent: 'space-between', fontSize: '10px', color: props.disabled ? themeProvider.theme.text.disabled : state.validity?.valid === false || validationResult?.isValid === false ? themeProvider.theme.palette.error.main : state.focused ? themeProvider.theme.text.primary : themeProvider.theme.text.secondary, marginBottom: '1em', padding: '1em', borderRadius: '5px', background: props.variant === 'contained' ? themeProvider .getRgbFromColorString(state.validity?.valid === false || validationResult?.isValid === false ? themeProvider.theme.palette.error.main : themeProvider.theme.palette[props.defaultColor || 'primary'].main) .update('a', state.focused ? 0.1 : 0.2) .toString() : 'transparent', boxShadow: props.variant === 'outlined' || props.variant === 'contained' ? `0 0 0 1px ${state.validity?.valid === false || validationResult?.isValid === false ? themeProvider.theme.palette.error.main : state.focused ? themeProvider.theme.palette[props.defaultColor || 'primary'].main : themeProvider.theme.text.primary}` : 'none', filter: props.disabled ? 'grayscale(100%)' : 'none', opacity: props.disabled ? '0.5' : '1', transition: 'color 0.2s ease-in-out, filter 0.2s ease-in-out, opacity 0.2s ease-in-out, border-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out', ...props.labelProps?.style, }; }; const getDefaultMessagesForValidityState = (state) => { if (!state.valid) { if (state.valueMissing) { return 'Value is required'; } if (state.typeMismatch) { return 'Value is not valid'; } if (state.patternMismatch) { return 'Value does not match the pattern'; } if (state.tooLong) { return 'Value is too long'; } if (state.tooShort) { return 'Value is too short'; } if (state.rangeUnderflow) { return 'Value is too low'; } if (state.rangeOverflow) { return 'Value is too high'; } if (state.stepMismatch) { return 'Value is not a valid step'; } if (state.badInput) { return 'Value is not valid'; } } }; export const Input = Shade({ shadowDomName: 'shade-input', constructed: ({ injector, element }) => { if (injector.cachedSingletons.has(FormService)) { const input = element.querySelector('input'); const formService = injector.getInstance(FormService); formService.inputs.add(input); return () => formService.inputs.delete(input); } }, render: ({ props, injector, useObservable, element }) => { const themeProvider = injector.getInstance(ThemeProviderService); const updateState = (newState) => { const label = element.querySelector('label'); const input = element.querySelector('input'); newState.value = input?.value || newState.value; newState.validity = input?.validity || newState.validity; newState.validity.toJSON = () => { return { valid: newState.validity.valid, valueMissing: newState.validity.valueMissing, typeMismatch: newState.validity.typeMismatch, patternMismatch: newState.validity.patternMismatch, tooLong: newState.validity.tooLong, tooShort: newState.validity.tooShort, rangeUnderflow: newState.validity.rangeUnderflow, rangeOverflow: newState.validity.rangeOverflow, stepMismatch: newState.validity.stepMismatch, badInput: newState.validity.badInput, }; }; const validationResult = props.getValidationResult?.({ state: newState }); if (validationResult?.isValid === false || newState.validity?.valid === false) { element.setAttribute('data-validation-failed', 'true'); } else { element.removeAttribute('data-validation-failed'); } attachStyles(label, { style: getLabelStyle({ themeProvider, props, state: newState, validationResult }) }); const helper = element.querySelector('span.helperText'); const helperNode = (validationResult?.isValid === false && validationResult?.message) || props.getHelperText?.({ state: newState, validationResult }) || getDefaultMessagesForValidityState(newState.validity) || ''; helper?.replaceChildren(helperNode); const startIcon = element.querySelector('span.startIcon'); startIcon?.replaceChildren(props.getStartIcon?.({ state: newState, validationResult }) || ''); const endIcon = element.querySelector('span.endIcon'); endIcon?.replaceChildren(props.getEndIcon?.({ state: newState, validationResult }) || ''); if (injector.cachedSingletons.has(FormService)) { const formService = injector.getInstance(FormService); formService.setFieldState(props.name, validationResult || { isValid: true }, newState.validity); } }; const [state, setState] = useObservable('inputState', new ObservableValue({ value: props.value || '', focused: props.autofocus || false, validity: element.querySelector('input')?.validity || {}, element, }), { onChange: updateState }); return (createComponent("label", { ...props.labelProps, style: getLabelStyle({ props, state, themeProvider }) }, props.labelTitle, createComponent("div", { style: { display: 'flex', alignItems: 'center', width: '100%', } }, props.getStartIcon ? createComponent("span", { className: "startIcon" }, props.getStartIcon?.({ state })) : null, createComponent("input", { oninvalid: (ev) => { ev.preventDefault(); const el = ev.target; setState({ ...state, validity: el.validity }); }, onchange: function (ev) { const el = ev.target; const newValue = el.value; setState({ ...state, value: newValue, validity: el?.validity }); props.onTextChange?.(newValue); props?.onchange?.call(this, ev); }, onfocus: (ev) => { const el = ev.target; setState({ ...state, focused: true, validity: el.validity }); }, onblur: (ev) => { const el = ev.target; setState({ ...state, focused: false, validity: el.validity }); }, ...props, style: { color: 'inherit', border: 'none', backgroundColor: 'transparent', outline: 'none', fontSize: '12px', width: '100%', textOverflow: 'ellipsis', padding: '0', marginTop: '0.6em', marginBottom: '0.4em', flexGrow: '1', ...props.style, }, value: state.value }), props.getEndIcon ? createComponent("span", { className: "endIcon" }, props.getEndIcon({ state })) : null), createComponent("span", { className: "helperText" }, props.getHelperText?.({ state })))); }, }); //# sourceMappingURL=input.js.map