adwaita-web
Version:
A GTK inspired toolkit designed to build awesome web apps
141 lines (140 loc) • 4.24 kB
JavaScript
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
};