@furystack/shades-common-components
Version:
176 lines • 8.63 kB
JavaScript
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