@hitachivantara/uikit-react-lab
Version:
Contributed React components to UI Kit by the community.
137 lines (136 loc) • 4.22 kB
JavaScript
import { useClasses } from "./Blade.styles.js";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { HvTypography, setId, useControlled, useDefaultProps, useUniqueId } from "@hitachivantara/uikit-react-core";
import { jsx, jsxs } from "react/jsx-runtime";
//#region src/Blade/Blade.tsx
/**
* A blade is a design element that expands horizontally to reveal information, similar to an accordion.
*
* It is usually stacked horizontally with other blades and works best when used within a flex container.
* The `HvBlades` component is recommended for this purpose, as it also provides better management of the
* blades' expanded state.
*/
var HvBlade = (props) => {
const { id: idProp, className, classes: classesProp, expanded, defaultExpanded = false, label, labelVariant = "label", headingLevel, onChange, disabled = false, children, fullWidth, buttonProps, containerProps, ...others } = useDefaultProps("HvBlade", props);
const { classes, cx } = useClasses(classesProp);
const [isExpanded, setIsExpanded] = useControlled(expanded, Boolean(defaultExpanded));
const handleAction = useCallback((event) => {
if (!disabled) {
onChange?.(event, !isExpanded);
setIsExpanded(!isExpanded);
return true;
}
return false;
}, [
disabled,
onChange,
isExpanded,
setIsExpanded
]);
const handleClick = useCallback((event) => {
handleAction(event);
event.preventDefault();
event.stopPropagation();
}, [handleAction]);
const handleKeyDown = useCallback((event) => {
let isEventHandled = false;
const { key } = event;
if (event.altKey || event.ctrlKey || event.metaKey || event.currentTarget !== event.target) return;
switch (key) {
case "Enter":
case " ":
isEventHandled = handleAction(event);
break;
default: return;
}
if (isEventHandled) {
event.preventDefault();
event.stopPropagation();
}
}, [handleAction]);
const id = useUniqueId(idProp);
const bladeHeaderId = setId(id, "button");
const bladeContainerId = setId(id, "container");
const bladeHeader = useMemo(() => {
const buttonLabel = typeof label === "function" ? label(isExpanded) : label;
const bladeButton = /* @__PURE__ */ jsx(HvTypography, {
id: bladeHeaderId,
component: "div",
role: "button",
className: cx(classes.button, {
[classes.textOnlyLabel]: typeof buttonLabel === "string",
[classes.disabled]: disabled
}),
style: { flexShrink: headingLevel === void 0 ? 0 : void 0 },
disabled,
tabIndex: 0,
onKeyDown: handleKeyDown,
onClick: handleClick,
variant: labelVariant,
"aria-expanded": isExpanded,
"aria-controls": bladeContainerId,
...buttonProps,
children: buttonLabel
});
return headingLevel === void 0 ? bladeButton : /* @__PURE__ */ jsx(HvTypography, {
component: `h${headingLevel}`,
variant: labelVariant,
className: classes.heading,
style: { flexShrink: 0 },
children: bladeButton
});
}, [
bladeContainerId,
bladeHeaderId,
buttonProps,
classes.button,
classes.disabled,
classes.heading,
classes.textOnlyLabel,
cx,
disabled,
handleClick,
handleKeyDown,
headingLevel,
isExpanded,
label,
labelVariant
]);
const bladeRef = useRef(null);
const [maxWidth, setMaxWidth] = useState(void 0);
useEffect(() => {
if (!bladeRef.current) return;
const resizeObserver = new ResizeObserver((entries) => {
setMaxWidth(entries[0].target.clientWidth);
});
resizeObserver.observe(bladeRef.current.parentElement ?? bladeRef.current);
return () => {
resizeObserver.disconnect();
};
}, [isExpanded]);
const { style: containerStyle, ...otherContainerProps } = containerProps || {};
return /* @__PURE__ */ jsxs("div", {
ref: bladeRef,
id: idProp,
className: cx(classes.root, className, {
[classes.fullWidth]: fullWidth,
[classes.expanded]: isExpanded
}),
...others,
children: [bladeHeader, /* @__PURE__ */ jsx("div", {
id: bladeContainerId,
role: "region",
"aria-labelledby": bladeHeaderId,
className: classes.container,
hidden: !isExpanded,
style: {
...containerStyle,
maxWidth: isExpanded ? maxWidth : 0
},
...otherContainerProps,
children
})]
});
};
//#endregion
export { HvBlade };