reblend-ui
Version:
Utilities for creating robust overlay components
121 lines (119 loc) • 3.65 kB
JavaScript
"use strict";
exports.__esModule = true;
exports.default = void 0;
exports.renderTransition = renderTransition;
exports.useTransition = useTransition;
var _reblendHooks = require("reblend-hooks");
var _reblendjs = require("reblendjs");
var _NoopTransition = require("./NoopTransition");
var _RTGTransition = require("./RTGTransition");
var _utils = require("./utils");
function useTransition({
in: inProp,
onTransition
}) {
const ref = (0, _reblendjs.useRef)(null);
const isInitialRef = (0, _reblendjs.useRef)(true);
const handleTransition = (0, _reblendHooks.useEventCallback)(onTransition);
useIsomorphicEffect(() => {
if (!ref.current) {
return undefined;
}
let stale = false;
handleTransition({
in: inProp,
element: ref.current,
initial: isInitialRef.current,
isStale: () => stale
});
return () => {
stale = true;
};
}, [inProp, handleTransition]);
useIsomorphicEffect(() => {
isInitialRef.current = false;
// this is for strict mode
return () => {
isInitialRef.current = true;
};
}, []);
return ref;
}
/**
* Adapts an imperative transition function to a subset of the RTG `<Transition>` component API.
*
* ImperativeTransition does not support mounting options or `appear` at the moment, meaning
* that it always acts like: `mountOnEnter={true} unmountOnExit={true} appear={true}`
*/
class ImperativeTransition extends Reblend {
static ELEMENT_NAME = "ImperativeTransition";
constructor() {
super();
}
async initState() {
const [exited, setExited] = _reblendjs.useState.bind(this)(!this.props.inProp, "exited");
// TODO: I think this needs to be in an effect
this.state.exited = exited;
this.state.setExited = setExited;
if (this.props.inProp && this.state.exited) {
this.state.setExited(false);
}
const ref = useTransition.bind(this)({
in: !!this.props.inProp,
onTransition: options => {
const onFinish = () => {
if (options.isStale()) return;
if (options.in) {
this.props.onEntered?.(options.element, options.initial);
} else {
this.state.setExited(true);
this.props.onExited?.(options.element);
}
};
Promise.resolve(this.props.transition(options)).then(onFinish, error => {
if (!options.in) this.state.setExited(true);
throw error;
});
}
});
this.state.ref = ref;
const combinedRef = _reblendHooks.useMergedRefs.bind(this)(this.state.ref, (0, _utils.getChildRef)(this.props.children));
this.state.combinedRef = combinedRef;
}
async initProps({
children,
in: inProp,
onExited,
onEntered,
transition
}) {
this.props = {};
this.props.children = children;
this.props.inProp = inProp;
this.props.onExited = onExited;
this.props.onEntered = onEntered;
this.props.transition = transition;
}
async html() {
return this.state.exited && !this.props.inProp ? null : (0, _reblendjs.cloneElement)(this.props.children, {
ref: this.state.combinedRef
});
}
}
/* @Reblend: Transformed from function to class */
exports.default = ImperativeTransition;
function renderTransition(component, runTransition, props) {
if (component) {
return Reblend.construct.bind(this)(_RTGTransition.default, {
...props,
component: component
});
}
if (runTransition) {
return Reblend.construct.bind(this)(ImperativeTransition, {
...props,
transition: runTransition
});
}
return Reblend.construct.bind(this)(_NoopTransition.default, props);
}