UNPKG

adwaita-web

Version:

A GTK inspired toolkit designed to build awesome web apps

141 lines (140 loc) 4.24 kB
import cx from "clsx"; import { equals } from "rambda"; import React from "react"; import { PanEnd, PanStart } from "../icons"; import { Label } from "./Label"; class Expander extends React.Component { static defaultProps = { transition: "vertical", iconPosition: "after", contents: false }; contentRef; state; constructor(props) { super(props); this.contentRef = React.createRef(); this.state = { open: false, containerStyle: {} }; } setOpen = (open) => { this.setState({ open }); }; componentDidUpdate() { this.updateDimensions(); } updateDimensions() { const { size, fitContent } = this.props; const property = this.getProperty(); let value = size ?? 100; if (this.contentRef.current) { const rect = this.contentRef.current.getBoundingClientRect(); value = rect[property]; const style = {}; style[property] = value; if (fitContent) { const inverseProperty = getInverseProperty(property); style[inverseProperty] = rect[inverseProperty]; } if (!equals(style, this.state.containerStyle)) this.setState({ containerStyle: style }); } } isOpen() { return this.props.open ?? this.state.open; } getProperty() { const { transition } = this.props; switch (transition) { case "vertical": return "height"; case "horizontal": return "width"; default: throw new Error("unreachable"); } } getContainerStyle() { const property = this.getProperty(); const open = this.isOpen(); return open ? this.state.containerStyle : { ...this.state.containerStyle, [property]: 0 }; } onRef = (ref) => { this.contentRef.current = ref; if (ref) this.updateDimensions(); }; render() { const { children, className, contents, open: openProp, trigger: triggerProp, label, transition, size, fitContent, iconPosition, onChange, ...rest } = this.props; const open = this.isOpen(); const setOpen = openProp !== void 0 ? onChange ?? this.setOpen : this.setOpen; const toggle = () => setOpen(!open); const property = this.getProperty(); const contentStyle = size === void 0 ? void 0 : { [property]: size }; const containerStyle = this.getContainerStyle(); const triggerClassName = cx("Expander__button", { expanded: open }); const trigger = triggerProp ? React.Children.map(typeof triggerProp === "function" ? triggerProp({ toggle }) : triggerProp, (child) => React.cloneElement(child, { className: cx(child.props.className, triggerClassName), onClick: child.props.onClick || toggle })) : !label ? null : /* @__PURE__ */ React.createElement("button", { type: "button", className: triggerClassName, onClick: toggle }, iconPosition === "before" && /* @__PURE__ */ React.createElement(PanEnd, { containerProps: { className: "arrow-before" } }), /* @__PURE__ */ React.createElement(Label, null, label), iconPosition === "after" && /* @__PURE__ */ React.createElement(PanStart, { containerProps: { className: "arrow-after" } })); const container = /* @__PURE__ */ React.createElement("div", { className: "Expander__container", style: containerStyle }, /* @__PURE__ */ React.createElement("div", { className: "Expander__content", style: contentStyle, ref: this.onRef }, children)); if (contents) return /* @__PURE__ */ React.createElement(React.Fragment, null, trigger, /* @__PURE__ */ React.createElement("div", { className: cx("Expander", className, transition, { open, "fit-content": fitContent }), ...rest }, container)); return /* @__PURE__ */ React.createElement("div", { className: cx("Expander", className, transition, { open, "fit-content": fitContent }), ...rest }, trigger, container); } } function getInverseProperty(p) { switch (p) { case "width": return "height"; case "height": return "width"; default: throw new Error("unreachable"); } } export { Expander };