@adaptui/react
Version:
Collection of headless components/hooks that are accessible, composable, customizable from low level to build your own UI & Design System powered by Reakit
177 lines (152 loc) • 8.15 kB
JavaScript
var _excluded = ["direction", "contentSize", "easing", "state"];
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
import * as React from "react";
import { flushSync } from "react-dom";
import { useRole } from "ariakit";
import { useEvent, useForkRef, useId, useUpdateEffect } from "ariakit-utils/hooks";
import { createComponent, createElement, createHook } from "ariakit-utils/system";
import raf from "raf";
import { getAutoSizeDuration, getElementHeight, getElementWidth } from "./__utils";
/**
* A component hook that returns props that can be passed to `Role` or any other
* Ariakit component to render an element that can be shown or hidden.
* @see https://ariakit.org/components/disclosure
* @example
* ```jsx
* const state = useDisclosureState();
* const props = useDisclosureCollapsibleContent({ state });
* <Disclosure state={state}>Disclosure</Disclosure>
* <Role {...props}>Content</Role>
* ```
*/
export var useDisclosureCollapsibleContent = createHook(_ref => {
var {
direction = "vertical",
contentSize = 0,
easing = "cubic-bezier(0.4, 0, 0.2, 1)",
state
} = _ref,
props = _objectWithoutProperties(_ref, _excluded);
var id = useId(props.id);
var contentRef = React.useRef(null);
var isVertical = direction === "vertical";
var currentSize = isVertical ? "height" : "width";
var getCurrentSizeStyle = React.useCallback(size => ({
[currentSize]: "".concat(size, "px")
}), [currentSize]);
var collapsedStyles = React.useMemo(() => {
return _objectSpread(_objectSpread({}, getCurrentSizeStyle(contentSize)), {}, {
overflow: "hidden"
});
}, [contentSize, getCurrentSizeStyle]);
var [styles, setStylesRaw] = React.useState(state.visible ? {} : collapsedStyles);
var setStyles = newStyles => {
// We rely on reading information from layout
// at arbitrary times, so ensure all style changes
// happen before we might attempt to read them.
flushSync(() => {
setStylesRaw(newStyles);
});
};
var mergeStyles = React.useCallback(newStyles => {
setStyles(oldStyles => _objectSpread(_objectSpread({}, oldStyles), newStyles));
}, []);
function getTransitionStyles(size) {
var _duration = props.duration || getAutoSizeDuration(size);
return {
transition: "".concat(currentSize, " ").concat(_duration, "ms ").concat(easing)
};
}
useUpdateEffect(() => {
if (state.visible) {
raf(() => {
var _props$onExpandStart;
(_props$onExpandStart = props.onExpandStart) === null || _props$onExpandStart === void 0 ? void 0 : _props$onExpandStart.call(props);
mergeStyles({
willChange: "".concat(currentSize),
overflow: "hidden"
});
raf(() => {
var size = isVertical ? getElementHeight(contentRef) : getElementWidth(contentRef);
mergeStyles(_objectSpread(_objectSpread({}, getTransitionStyles(size)), isVertical ? {
height: size
} : {
width: size
}));
});
});
} else {
raf(() => {
var _props$onCollapseStar;
(_props$onCollapseStar = props.onCollapseStart) === null || _props$onCollapseStar === void 0 ? void 0 : _props$onCollapseStar.call(props);
var size = isVertical ? getElementHeight(contentRef) : getElementWidth(contentRef);
mergeStyles(_objectSpread(_objectSpread({
willChange: "".concat(currentSize)
}, isVertical ? {
height: size
} : {
width: size
}), getTransitionStyles(size)));
raf(() => {
mergeStyles(_objectSpread(_objectSpread({}, getCurrentSizeStyle(contentSize)), {}, {
overflow: "hidden"
}));
});
});
}
}, [state.visible]);
var onTransitionEndProp = props.onTransitionEnd;
var onTransitionEnd = useEvent(event => {
onTransitionEndProp === null || onTransitionEndProp === void 0 ? void 0 : onTransitionEndProp(event);
if (event.defaultPrevented) return; // Sometimes onTransitionEnd is triggered by another transition,
// such as a nested collapse panel transitioning. But we only
// want to handle this if this component's element is transitioning
if (event.target !== contentRef.current || event.propertyName !== currentSize) {
return;
} // The height comparisons below are a final check before
// completing the transition
// Sometimes this callback is run even though we've already begun
// transitioning the other direction
// The conditions give us the opportunity to bail out,
// which will prevent the collapsed content from flashing on the screen
var stylesSize = isVertical ? styles.height : styles.width;
if (state.visible) {
var _props$onExpandEnd;
var size = isVertical ? getElementHeight(contentRef) : getElementWidth(contentRef); // If the height at the end of the transition
// matches the height we're animating to,
if (size === stylesSize) {
setStyles({});
} else {
// If the heights don't match, this could be due the height
// of the content changing mid-transition
mergeStyles(_objectSpread({}, getCurrentSizeStyle(contentSize)));
}
(_props$onExpandEnd = props.onExpandEnd) === null || _props$onExpandEnd === void 0 ? void 0 : _props$onExpandEnd.call(props); // If the height we should be animating to matches the collapsed height,
// it's safe to apply the collapsed overrides
} else if (stylesSize === "".concat(contentSize, "px")) {
var _props$onCollapseEnd;
setStyles(collapsedStyles);
(_props$onCollapseEnd = props.onCollapseEnd) === null || _props$onCollapseEnd === void 0 ? void 0 : _props$onCollapseEnd.call(props);
}
});
var style = _objectSpread(_objectSpread({}, styles), props.style);
props = _objectSpread(_objectSpread({
id,
hidden: !state.mounted
}, props), {}, {
ref: useForkRef(id ? state.setContentElement : null, contentRef, props.ref),
onTransitionEnd,
style
});
props = useRole(props);
return props;
});
export var DisclosureCollapsibleContent = createComponent(props => {
var htmlProps = useDisclosureCollapsibleContent(props);
return createElement("div", htmlProps);
});
//# sourceMappingURL=disclosure-collapsible-content.js.map