@hhgtech/hhg-components
Version:
Hello Health Group common components
417 lines (398 loc) • 16.5 kB
JavaScript
;
var tslib_es6 = require('./tslib.es6-92cccef3.js');
var React = require('react');
var cn = require('classnames');
var dayjs = require('dayjs');
var react = require('@emotion/react');
var styled = require('@emotion/styled');
var miscTheme = require('./miscTheme.js');
var index = require('./index-1ee4ebb8.js');
var Locale = require('./Locale-59ccf941.js');
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
var React__default = /*#__PURE__*/_interopDefault(React);
var cn__default = /*#__PURE__*/_interopDefault(cn);
var dayjs__default = /*#__PURE__*/_interopDefault(dayjs);
var styled__default = /*#__PURE__*/_interopDefault(styled);
const KEYCODE = {
BACKSPACE: 8,
SPACEBAR: 32,
ZERO: 48,
};
const PATTERN_NUMBER = /^([0-9]*)$/;
const INPUT_DATE_PLACEHOLDER = {
DD: 'Day',
MM: 'Month',
YYYY: 'Year',
};
const INPUT_DATE_FORMAT_TYPING = {
DD: (day, cb) => {
if (!day) {
cb === null || cb === void 0 ? void 0 : cb('');
return '';
}
const dayNumber = parseInt(day);
if (dayNumber > 31) {
cb === null || cb === void 0 ? void 0 : cb(day[0]);
return day[0];
}
/** Day cannot be over number 31 */
if (dayNumber >= 4) {
const dayString = String(dayNumber).padStart(2, '0');
cb === null || cb === void 0 ? void 0 : cb(dayString);
return dayString;
}
cb === null || cb === void 0 ? void 0 : cb(day);
return day;
},
MM: (month, cb) => {
if (!month) {
cb === null || cb === void 0 ? void 0 : cb('');
return '';
}
const monthNumber = parseInt(month);
if (monthNumber > 12) {
cb === null || cb === void 0 ? void 0 : cb(month[0]);
return month[0];
}
/** Month cannot be over number 12 */
if (monthNumber >= 2) {
const monthString = String(monthNumber).padStart(2, '0');
cb === null || cb === void 0 ? void 0 : cb(monthString);
return monthString;
}
cb === null || cb === void 0 ? void 0 : cb(month);
return month;
},
YYYY: (year, cb) => {
if (!year) {
cb === null || cb === void 0 ? void 0 : cb('');
return '';
}
cb === null || cb === void 0 ? void 0 : cb(year);
return year;
},
};
const INPUT_DATE_DEFAULT_DISABLED = [false, true, true];
const getInputDateInputDisabled = (valueString) => {
return valueString.map((_, index) => {
if (index === 0) {
return false;
}
if (index !== 0 && !!valueString[index - 1]) {
return false;
}
return true;
});
};
const cssInputNotShrink = react.css `
max-width: 100%;
color: ${miscTheme.theme.colors.gray800};
font-size: var(--input-font-size, 16px);
font-weight: 400;
line-height: calc(var(--input-height, 56px) * 24 / 56);
opacity: 0;
transition: 0.3s linear opacity;
vertical-align: middle;
&::placeholder {
color: transparent;
font-weight: 400;
}
`;
const cssInputShrink = react.css `
opacity: 1;
line-height: calc(var(--input-height, 56px) * 24 / 56);
transition: 0.3 linear opacity;
&::placeholder {
color: ${miscTheme.theme.colors.gray600};
}
`;
const cssPlaceholderNotShrink = react.css `
position: absolute;
top: calc(var(--input-height, 56px) / 2);
left: var(--input-padding, 16px);
color: ${miscTheme.theme.colors.gray600};
transform: translateY(-50%);
transition: 0.125s linear all;
vertical-align: middle;
`;
const cssPlaceholderShrink = react.css `
top: calc(var(--input-height, 56px) * 7 / 56);
font-size: calc(var(--input-font-size, 16px) * 12 / 16);
line-height: calc(var(--input-height, 56px) * 18 / 56);
transform: translateY(0);
transition: 0.125s linear all;
`;
const StyledInputDate = styled__default["default"].div `
--input-gap: 8px;
--input-height: 56px;
--input-max-width: 320px;
--input-font-size: 16px;
--input-line-height: 24px;
--input-padding-x: 16px;
--input-padding-y: 16px;
--input-border-radius: 8px;
--input-border-color: ${miscTheme.theme.colors.gray200};
display: flex;
gap: var(--input-gap, 8px);
max-width: var(--input-max-width, 320px);
font-weight: 400;
* {
box-sizing: border-box;
}
.input_date-input_container {
position: relative;
display: flex;
align-items: center;
width: 100%;
height: var(--input-height, 56px);
max-width: calc((100% / 3) - (var(--input-gap, 8px) * 2 / 3));
line-height: var(--input-line-height, 24px);
padding: var(--input-padding-y, 16px) var(--input-padding-x, 16px);
border-radius: var(--input-border-radius, 8px);
border: 1px solid var(--input-border-color, ${miscTheme.theme.colors.gray200});
.input_date-input_placeholder {
${cssPlaceholderNotShrink}
}
.input_date-input {
${cssInputNotShrink}
}
&:has([disabled]) {
pointer-events: none;
}
&:focus-within,
&.input_date-input_shrink {
--input-padding-y: 8px;
align-items: flex-end;
.input_date-input_placeholder {
${cssPlaceholderShrink}
}
.input_date-input {
${cssInputShrink}
}
}
&.input_date-input_active {
border-color: ${miscTheme.theme.colors.primary600};
box-shadow: 0px 0px 5px ${miscTheme.theme.colors.primary400};
transition: 0.25s linear all;
}
&.input_date-input_error {
border-color: ${miscTheme.theme.colors.red600};
}
}
`;
// eslint-disable-next-line @typescript-eslint/no-var-requires
const customParseFormat = require('dayjs/plugin/customParseFormat');
dayjs__default["default"].extend(customParseFormat);
const { BACKSPACE, SPACEBAR, ZERO } = KEYCODE;
const formatInputDateValue = (value, format) => {
if (typeof value === 'undefined') {
return null;
}
if (typeof value === 'string') {
const dayInJS = dayjs__default["default"](value, format, true);
if (dayInJS.isValid()) {
return dayInJS.toDate();
}
return null;
}
const dayInJS = dayjs__default["default"](value);
if (dayInJS.isValid()) {
return dayInJS.toDate();
}
return null;
};
const formatInputDateValueString = (value, format) => {
if (!value) {
return [undefined, undefined, undefined];
}
const dayInJS = dayjs__default["default"](value);
return dayInJS.format(format).split('/');
};
const InputDate = React.forwardRef((props, ref) => {
const { id, name, locale = Locale.LOCALE.Vietnam, className, style, defaultValue: defaultValueProps, value: valueProps, onChange: onChangeProps, error, disabled: disabledProps, labelTuples, placeholderTuples, onFocus, onBlur, autoComplete = 'off' } = props, restProps = tslib_es6.__rest(props, ["id", "name", "locale", "className", "style", "defaultValue", "value", "onChange", "error", "disabled", "labelTuples", "placeholderTuples", "onFocus", "onBlur", "autoComplete"]);
const [activeInput, setActiveInput] = React.useState(-1);
const inputRef = React.useRef([]);
const [value, setValue] = React.useState(formatInputDateValue(defaultValueProps || valueProps, index.ISO_FORMAT[locale].dateFormat));
const [valueString, setValueString] = React.useState(['', '', '']);
const [disabled, setDisabled] = React.useState(() => getInputDateInputDisabled(formatInputDateValueString(defaultValueProps || valueProps, index.ISO_FORMAT[locale].dateFormat)));
const { dateFormat } = React.useMemo(() => {
return (index.ISO_FORMAT === null || index.ISO_FORMAT === void 0 ? void 0 : index.ISO_FORMAT[locale]) || index.ISO_FORMAT['vi-VN'];
}, [locale]);
const orders = React.useMemo(() => dateFormat.split('/'), [dateFormat]);
const defaultValue = React.useMemo(() => formatInputDateValueString(defaultValueProps, dateFormat), [defaultValueProps, dateFormat]);
const label = React.useMemo(() => orders.map((keyOrd, idx) => (labelTuples === null || labelTuples === void 0 ? void 0 : labelTuples[idx]) || INPUT_DATE_PLACEHOLDER[keyOrd]), [labelTuples]);
const placeholder = React.useMemo(() => orders.map((keyOrd, idx) => (placeholderTuples === null || placeholderTuples === void 0 ? void 0 : placeholderTuples[idx]) || keyOrd), [placeholderTuples]);
const focusPrevInput = React.useCallback(() => {
var _a, _b;
if (activeInput === 0 || activeInput === -1) {
return;
}
const curInput = (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a[activeInput];
if (curInput) {
curInput.blur();
}
const prevInput = (_b = inputRef.current) === null || _b === void 0 ? void 0 : _b[activeInput - 1];
if (prevInput) {
prevInput.focus();
}
}, [activeInput]);
const focusNextInput = React.useCallback(() => {
var _a, _b;
if (activeInput === orders.length - 1) {
return;
}
const curInput = (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a[activeInput];
if (curInput) {
curInput.blur();
}
const nextInput = (_b = inputRef.current) === null || _b === void 0 ? void 0 : _b[activeInput + 1];
if (nextInput) {
nextInput.focus();
}
}, [activeInput, orders]);
const changeCodeAtFocus = React.useCallback(() => {
let count = 0;
function changeFocus() {
const target = inputRef.current[activeInput];
const nextTarget = inputRef.current[activeInput + 1];
const disabledNext = disabled[activeInput + 1];
if (!nextTarget || count === 1) {
return;
}
if (disabledNext) {
requestAnimationFrame(changeFocus);
count++;
}
if (target.value.length === target.maxLength) {
focusNextInput();
}
}
requestAnimationFrame(changeFocus);
}, [...disabled, activeInput, focusNextInput]);
const onChange = React.useCallback((valStr) => {
const valString = valStr || valueString;
const dayString = orders
.map((_, index) => {
const val = valString[index];
return val;
})
.filter(Boolean)
.join('/');
const dayInJS = dayjs__default["default"](dayString, dateFormat, true);
if (dayInJS.isValid()) {
setValue(dayInJS.toDate());
onChangeProps === null || onChangeProps === void 0 ? void 0 : onChangeProps(dayInJS.toDate(), dayString);
}
else {
setValue(null);
onChangeProps === null || onChangeProps === void 0 ? void 0 : onChangeProps(dayString, dayString);
}
}, [orders, dateFormat, ...valueString]);
const inputOnChange = React.useCallback((index) => ((e) => {
const val = e.target.value;
const valString = [...valueString];
if (!val) {
valString[index] = '';
setValueString(valString);
onChange(valString);
if (index === 0) {
setDisabled(INPUT_DATE_DEFAULT_DISABLED);
}
return;
}
if (!val.trim().match(PATTERN_NUMBER)) {
return;
}
const inputType = orders[index];
INPUT_DATE_FORMAT_TYPING[inputType](val, (newVal) => {
valString[index] = newVal;
setDisabled(getInputDateInputDisabled(valString));
setValueString(valString);
changeCodeAtFocus();
});
onChange(valString);
}), [...valueString, orders, changeCodeAtFocus, onChange]);
const inputOnFocus = React.useCallback((e) => {
const target = e.target;
const inputIndex = inputRef.current.findIndex((input) => input === target);
if (inputIndex !== -1) {
setActiveInput(inputIndex);
}
if (inputIndex === 0) {
onFocus === null || onFocus === void 0 ? void 0 : onFocus(e);
}
}, [...valueString, orders, onFocus]);
const inputOnBlur = React.useCallback((e) => {
setActiveInput(-1);
const target = e.target;
const inputIndex = inputRef.current.findIndex((input) => input === target);
if (inputIndex !== -1) {
const newVal = target.value;
const valString = [...valueString];
if (newVal !== '') {
valString[inputIndex] = String(newVal).padStart(target.maxLength, '0');
setDisabled(getInputDateInputDisabled(valString));
setValueString(valString);
onChange(valString);
}
}
onBlur === null || onBlur === void 0 ? void 0 : onBlur(e);
}, [...valueString, orders, onChange, onBlur]);
// Handle cases of backspace, delete, left arrow, right arrow, space
const inputOnKeyDown = React.useCallback((e) => {
const target = inputRef.current[activeInput];
if (target.value === '0' && (e.keyCode === ZERO || e.key === '0')) {
e.preventDefault();
}
if (e.keyCode === BACKSPACE || e.key === 'Backspace') {
if (!target.value) {
focusPrevInput();
}
}
if (e.keyCode === SPACEBAR ||
e.key === ' ' ||
e.key === 'Spacebar' ||
e.key === 'Space') {
e.preventDefault();
}
}, [activeInput, focusPrevInput, focusNextInput]);
const onClick = React.useCallback((e) => {
const target = e.target;
const targetIndex = inputRef.current.findIndex((input) => input === target);
if (targetIndex === -1) {
const focusIndex = !!value ? inputRef.current.length - 1 : 0;
inputRef.current[focusIndex].focus();
setActiveInput(focusIndex);
return;
}
inputRef.current[targetIndex].focus();
setActiveInput(targetIndex);
}, [inputRef.current, defaultValue, value]);
React.useEffect(() => {
setValue(formatInputDateValue(valueProps, dateFormat));
}, [valueProps, dateFormat]);
React.useEffect(() => {
if (value) {
const valueStringUpdated = formatInputDateValueString(value, dateFormat);
setValueString(valueStringUpdated);
setDisabled(getInputDateInputDisabled(valueStringUpdated));
}
}, [value, dateFormat]);
React.useImperativeHandle(ref, () => inputRef.current[0], []);
return (React__default["default"].createElement(StyledInputDate, Object.assign({ className: className, style: style, onClick: onClick }, restProps), orders.map((inputName, idxOrd) => {
const inputLabel = label[idxOrd];
const inputDefaultValue = defaultValue === null || defaultValue === void 0 ? void 0 : defaultValue[idxOrd];
const inputValue = valueString[idxOrd];
const inputDisabled = disabled[idxOrd];
const inputPlaceholder = placeholder[idxOrd];
const inputMaxLength = inputName.length;
return (React__default["default"].createElement("div", { key: inputName, className: cn__default["default"]('input_date-input_container', {
'input_date-input_active': activeInput === idxOrd,
'input_date-input_shrink': !!inputValue,
'input_date-input_error': error,
}) },
React__default["default"].createElement("span", { className: "input_date-input_placeholder" }, inputLabel),
React__default["default"].createElement("input", { ref: (ref) => (inputRef.current[idxOrd] = ref), name: `${id || ''}-${name || ''}-${inputName}`, type: "text", inputMode: "numeric", defaultValue: inputDefaultValue, value: inputValue, maxLength: inputMaxLength, disabled: disabledProps || inputDisabled, placeholder: inputPlaceholder, onChange: inputOnChange(idxOrd), onBlur: inputOnBlur, onFocus: inputOnFocus, onKeyDown: inputOnKeyDown, className: "input_date-input", autoComplete: autoComplete })));
})));
});
exports.InputDate = InputDate;