@kiwicom/orbit-components
Version:
Orbit-components is a React component library which provides developers with the easiest possible way of building Kiwi.com’s products.
137 lines (135 loc) • 6.84 kB
JavaScript
"use client";
import * as React from "react";
import cx from "clsx";
import { SIZE_OPTIONS } from "./consts";
import FormLabel from "../FormLabel";
import ErrorFormTooltip from "../ErrorFormTooltip";
import useRandomId, { useRandomIdSeed } from "../hooks/useRandomId";
import getFieldDataState from "../common/getFieldDataState";
import { getSpaceAfterClasses } from "../common/tailwind";
const findPropInChild = (propToFind, children) => React.Children.map(children, el => {
if (el.props && el.props[propToFind]) return el.props[propToFind];
return null;
}).filter(el => el != null);
const InputGroup = /*#__PURE__*/React.forwardRef(({
children,
label,
flex,
size = SIZE_OPTIONS.NORMAL,
help,
id,
error,
disabled,
dataTest,
spaceAfter,
helpClosable = true,
onFocus,
onBlur,
onChange,
onBlurGroup
}, ref) => {
const [active, setActive] = React.useState(false);
const [filled, setFilled] = React.useState(false);
const inputID = useRandomId();
const [tooltipShown, setTooltipShown] = React.useState(false);
const [tooltipShownHover, setTooltipShownHover] = React.useState(false);
const labelRef = React.useRef(null);
const iconRef = React.useRef(null);
const foundErrors = findPropInChild("error", children);
const foundHelp = findPropInChild("help", children);
const errorReal = error || foundErrors.length > 0 && foundErrors[0];
const helpReal = help || foundHelp.length > 0 && foundHelp[0];
const randomId = useRandomIdSeed();
const isFilled = React.useCallback(() => setFilled(findPropInChild("value", children).length > 0), [children]);
React.useEffect(() => {
isFilled();
}, [isFilled, label]);
const handleFocus = callBack => ev => {
setActive(true);
setTooltipShown(true);
if (onFocus) onFocus(ev);
if (callBack) callBack(ev);
};
const handleBlur = callBack => ev => {
isFilled();
setActive(false);
if (onBlur) onBlur(ev);
if (callBack) callBack(ev);
};
const handleChange = callBack => ev => {
isFilled();
if (onChange) onChange(ev);
if (callBack) callBack(ev);
};
const handleBlurGroup = ev => {
ev.persist();
if (onBlurGroup) {
setTimeout(() => {
setActive(isActive => {
if (!isActive) {
onBlurGroup(ev);
}
return isActive;
});
}, 50);
}
};
return /*#__PURE__*/React.createElement("div", {
ref: ref,
id: id,
"data-test": dataTest,
"data-state": getFieldDataState(!!errorReal),
"aria-labelledby": label != null ? inputID : undefined,
role: "group",
className: cx("relative flex w-full flex-col", spaceAfter != null && getSpaceAfterClasses(spaceAfter))
}, label && /*#__PURE__*/React.createElement(FormLabel, {
filled: filled,
id: inputID,
labelRef: labelRef,
error: !!errorReal,
help: !!helpReal,
iconRef: iconRef,
onMouseEnter: () => setTooltipShownHover(true),
onMouseLeave: () => setTooltipShownHover(false)
}, label), /*#__PURE__*/React.createElement("div", {
className: cx("text-normal h-form-box-normal duration-fast rounded-large tb:rounded-normal z-[1] w-full transition-shadow ease-in-out", active && "outline-blue-normal outline outline-2", disabled ? "bg-form-element-disabled-background" : "bg-form-element-background", !errorReal && "shadow-form-element", !errorReal && !disabled && "hover:shadow-form-element-hover", Boolean(errorReal) && "shadow-form-element-error", Boolean(errorReal) && !disabled && "hover:shadow-form-element-error-hover")
}, /*#__PURE__*/React.createElement("div", {
className: "relative flex",
onBlur: handleBlurGroup
}, React.Children.toArray(children).map((child, key) => {
// TODO: next cleanup iteration just remove this whole `flex` prop thing
// and cloning elements, and make children take care of their sizing themselves
const childFlex = Array.isArray(flex) && flex.length !== 1 ? flex[key] ?? flex[0] : flex;
const item = child;
return /*#__PURE__*/React.createElement("div", {
key: randomId(String(key)),
className: cx("orbit-input-group-child pe-xs last:p-0 [&_.orbit-input-field-fake-input]:hidden [&_.orbit-input-field-fake-input]:bg-transparent [&_.orbit-input-field-input~.orbit-input-field-fake-input]:shadow-none [&_.orbit-select-container_select]:bg-transparent [&_.orbit-select-container_select]:shadow-none [&_.orbit-select-container_select]:focus:outline-none",
// InputField:after
"[&_.orbit-input-field-input-container]:after:duration-fast [&_.orbit-input-field-input-container]:after:absolute [&_.orbit-input-field-input-container]:after:end-0 [&_.orbit-input-field-input-container]:after:top-1/2 [&_.orbit-input-field-input-container]:after:z-[1] [&_.orbit-input-field-input-container]:after:block [&_.orbit-input-field-input-container]:after:h-[24px] [&_.orbit-input-field-input-container]:after:-translate-y-1/2 [&_.orbit-input-field-input-container]:after:border-r [&_.orbit-input-field-input-container]:after:transition-colors [&_.orbit-input-field-input-container]:after:ease-in-out [&_.orbit-input-field-input-container]:last-of-type:after:content-none",
// Select:after
"[&_.orbit-select-container]:after:duration-fast [&_.orbit-select-container]:bg-transparent [&_.orbit-select-container]:after:absolute [&_.orbit-select-container]:after:end-0 [&_.orbit-select-container]:after:top-1/2 [&_.orbit-select-container]:after:z-[2] [&_.orbit-select-container]:after:block [&_.orbit-select-container]:after:h-[24px] [&_.orbit-select-container]:after:-translate-y-1/2 [&_.orbit-select-container]:after:border-r [&_.orbit-select-container]:after:transition-colors [&_.orbit-select-container]:after:ease-in-out [&_.orbit-select-container]:last-of-type:after:content-none", Boolean(errorReal) && !active ? "[&_.orbit-select-container]:after:border-input-error [&_.orbit-input-field-input-container]:after:border-input-error" : "[&_.orbit-select-container]:after:border-input [&_.orbit-input-field-input-container]:after:border-input", label != null && "[&_.orbit-form-label]:hidden"),
style: {
flex: childFlex
}
}, /*#__PURE__*/React.cloneElement(item, {
disabled: item.props.disabled || disabled,
size,
label: undefined,
onChange: handleChange(item.props.onChange),
onBlur: handleBlur(item.props.onBlur),
onFocus: handleFocus(item.props.onFocus),
// @ts-expect-error custom prop
insideInputGroup: true
}));
}))), /*#__PURE__*/React.createElement(ErrorFormTooltip, {
help: helpReal,
error: errorReal,
helpClosable: helpClosable,
inputSize: size,
onShown: setTooltipShown,
shown: tooltipShown || tooltipShownHover,
referenceElement: labelRef
}));
});
InputGroup.displayName = "InputGroup";
export default InputGroup;