@renderlesskit/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
193 lines (167 loc) • 8.57 kB
JavaScript
var _excluded = ["direction", "contentSize", "easing"],
_excluded2 = ["ref", "style", "onTransitionEnd"];
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; }
// Core Logic for transition is based on https://github.com/roginfarrer/react-collapsed
import * as React from "react";
import { flushSync } from "react-dom";
import { createComponent } from "reakit-system";
import { useBox } from "reakit";
import { useForkRef, useLiveRef, useUpdateEffect } from "reakit-utils";
import raf from "raf";
import { createComposableHook } from "../system";
import { DISCLOSURE_COLLAPSE_CONTENT_KEYS } from "./__keys";
import { getAutoSizeDuration, getElementHeight, getElementWidth } from "./helpers";
export var disclosureCollapseComposableContent = createComposableHook({
name: "DisclosureCollapseContent",
compose: useBox,
keys: DISCLOSURE_COLLAPSE_CONTENT_KEYS,
useOptions(options, htmlProps) {
var {
direction = "vertical",
contentSize = 0,
easing = "cubic-bezier(0.4, 0, 0.2, 1)"
} = options,
restOptions = _objectWithoutProperties(options, _excluded);
return _objectSpread({
direction,
contentSize,
easing
}, restOptions);
},
useProps(options, htmlProps) {
var {
contentSize,
visible,
direction,
duration,
easing,
onCollapseEnd,
onCollapseStart,
onExpandEnd,
onExpandStart
} = options;
var {
ref: htmlRef,
style: htmlStyle,
onTransitionEnd: htmlOnTransitionEnd
} = htmlProps,
restHtmlProps = _objectWithoutProperties(htmlProps, _excluded2);
var ref = React.useRef(null);
var onTransitionEndRef = useLiveRef(htmlOnTransitionEnd);
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(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 = duration || getAutoSizeDuration(size);
return {
transition: "".concat(currentSize, " ").concat(_duration, "ms ").concat(easing)
};
}
useUpdateEffect(() => {
if (visible) {
raf(() => {
onExpandStart === null || onExpandStart === void 0 ? void 0 : onExpandStart();
mergeStyles({
willChange: "".concat(currentSize),
overflow: "hidden"
});
raf(() => {
var size = isVertical ? getElementHeight(ref) : getElementWidth(ref);
mergeStyles(_objectSpread(_objectSpread({}, getTransitionStyles(size)), isVertical ? {
height: size
} : {
width: size
}));
});
});
} else {
raf(() => {
onCollapseStart === null || onCollapseStart === void 0 ? void 0 : onCollapseStart();
var size = isVertical ? getElementHeight(ref) : getElementWidth(ref);
mergeStyles(_objectSpread(_objectSpread({
willChange: "".concat(currentSize)
}, isVertical ? {
height: size
} : {
width: size
}), getTransitionStyles(size)));
raf(() => {
mergeStyles(_objectSpread(_objectSpread({}, getCurrentSizeStyle(contentSize)), {}, {
overflow: "hidden"
}));
});
});
}
}, [visible]);
var onTransitionEnd = React.useCallback(event => {
var _onTransitionEndRef$c;
(_onTransitionEndRef$c = onTransitionEndRef.current) === null || _onTransitionEndRef$c === void 0 ? void 0 : _onTransitionEndRef$c.call(onTransitionEndRef, 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 !== ref.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 (visible) {
var size = isVertical ? getElementHeight(ref) : getElementWidth(ref); // 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)));
}
onExpandEnd === null || onExpandEnd === void 0 ? void 0 : onExpandEnd(); // 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")) {
setStyles(collapsedStyles);
onCollapseEnd === null || onCollapseEnd === void 0 ? void 0 : onCollapseEnd();
}
}, [onTransitionEndRef, currentSize, isVertical, styles.height, styles.width, visible, contentSize, onExpandEnd, mergeStyles, getCurrentSizeStyle, collapsedStyles, onCollapseEnd]);
var style = _objectSpread(_objectSpread({}, styles), htmlStyle);
return _objectSpread({
ref: useForkRef(ref, htmlRef),
id: options.baseId,
"aria-hidden": !visible,
style,
onTransitionEnd
}, restHtmlProps);
}
});
export var useDisclosureCollapseContent = disclosureCollapseComposableContent();
export var DisclosureCollapseContent = createComponent({
as: "div",
memo: true,
useHook: useDisclosureCollapseContent
});
//# sourceMappingURL=DisclosureCollapseContent.js.map