react-fast-accordion
Version:
High performance 0 dependencies accordion for React
119 lines (117 loc) • 3.16 kB
JavaScript
// src/index.tsx
import React2, {
useEffect,
useRef,
useState
} from "react";
// src/ListItem.tsx
import React, { memo } from "react";
var ListItem = ({
id,
isOpen,
SummaryComponent,
DetailComponent,
...rest
}) => {
return /* @__PURE__ */ React.createElement("li", {
role: "button",
tabIndex: 0,
"aria-expanded": isOpen,
"aria-controls": `acc-content-${id}`,
id: id.toString(),
className: "acc-item"
}, /* @__PURE__ */ React.createElement(SummaryComponent, {
...rest,
isOpen
}), isOpen && /* @__PURE__ */ React.createElement("div", {
role: "definition",
className: "acc-content",
id: `acc-content-${id}`
}, /* @__PURE__ */ React.createElement(DetailComponent, {
...rest,
isOpen
})));
};
var ListItem_default = memo(ListItem);
// src/index.tsx
var Accordion = ({ items, multiExpand = true, ...rest }) => {
const [opened, setOpened] = useState({});
const listContainerRef = useRef(null);
const mutationCb = (list) => {
const contentItem = list[0].target.childNodes[1] ?? null;
if (!contentItem)
return;
if (contentItem.className !== "acc-content")
return;
const scrollHeight = contentItem.scrollHeight;
contentItem.animate({ maxHeight: `${scrollHeight}px`, opacity: 1 }, { duration: 100, easing: "ease-in", fill: "forwards" });
};
useEffect(() => {
if (!listContainerRef.current)
return;
const observer = new MutationObserver(mutationCb);
observer.observe(listContainerRef.current, {
childList: true,
subtree: true
});
return () => {
observer.disconnect();
};
}, []);
const closeAccordion = (id) => {
const contentItem = document.getElementById(`acc-content-${id}`);
if (!contentItem)
return;
contentItem.animate({ maxHeight: 0, opacity: 0 }, { duration: 100, easing: "ease-out" }).finished.then(() => {
contentItem.style.display = "none";
setOpened((prv) => {
const newObj = { ...prv };
delete newObj[id];
return newObj;
});
});
};
const clickHandler = (e) => {
let element = e.target;
if (element.parentElement?.tagName === "LI") {
element = element.parentElement;
}
if (element.tagName !== "LI")
return;
const id = element.getAttribute("id");
if (!id)
return;
const isOpen = opened[id];
if (isOpen) {
return closeAccordion(id);
}
setOpened((prv) => ({ ...prv, [id]: true }));
if (!multiExpand) {
const prvAccId = Object.keys(opened)[0];
closeAccordion(prvAccId);
}
};
const ariaHandler = (e) => {
if (e.key === " " || e.key === "Enter") {
clickHandler(e);
e.preventDefault();
}
};
return /* @__PURE__ */ React2.createElement("ul", {
onClick: clickHandler,
onKeyPress: ariaHandler,
ref: listContainerRef,
role: "list"
}, items.map(({ id, ...data }) => /* @__PURE__ */ React2.createElement(ListItem_default, {
id,
key: id,
isOpen: opened[id],
...data,
...rest
})));
};
var src_default = Accordion;
export {
src_default as default
};
//# sourceMappingURL=index.mjs.map