UNPKG

@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
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