reblend-ui
Version:
Utilities for creating robust overlay components
165 lines (162 loc) • 5.58 kB
JavaScript
"use strict";
exports.__esModule = true;
exports.default = void 0;
var _reblendjs = require("reblendjs");
var _dequal = require("dequal");
var _popper = require("./popper");
const disabledApplyStylesModifier = {
name: 'applyStyles',
enabled: false,
phase: 'afterWrite',
fn: () => undefined
};
// until docjs supports type exports...
const ariaDescribedByModifier = {
name: 'ariaDescribedBy',
enabled: true,
phase: 'afterWrite',
effect: ({
state
}) => () => {
const {
reference,
popper
} = state.elements;
if ('removeAttribute' in reference) {
const ids = (reference.getAttribute('aria-describedby') || '').split(',').filter(id => id.trim() !== popper.id);
if (!ids.length) reference.removeAttribute('aria-describedby');else reference.setAttribute('aria-describedby', ids.join(','));
}
},
fn: ({
state
}) => {
const {
popper,
reference
} = state.elements;
const role = popper.getAttribute('role')?.toLowerCase();
if (popper.id && role === 'tooltip' && 'setAttribute' in reference) {
const ids = reference.getAttribute('aria-describedby');
if (ids && ids.split(',').indexOf(popper.id) !== -1) {
return;
}
reference.setAttribute('aria-describedby', ids ? `${ids},${popper.id}` : popper.id);
}
}
};
const EMPTY_MODIFIERS = [];
/**
* Position an element relative some reference element using Popper.js
*
* @param referenceElement
* @param popperElement
* @param {object} options
* @param {object=} options.modifiers Popper.js modifiers
* @param {boolean=} options.enabled toggle the popper functionality on/off
* @param {string=} options.placement The popper element placement relative to the reference element
* @param {string=} options.strategy the positioning strategy
* @param {function=} options.onCreate called when the popper is created
* @param {function=} options.onUpdate called when the popper is updated
*
* @returns {UsePopperState} The popper state
*/
function usePopper(referenceElement, popperElement, {
enabled = true,
placement = 'bottom',
strategy = 'absolute',
modifiers = EMPTY_MODIFIERS,
...config
} = {}) {
const prevModifiers = _reblendjs.useRef.bind(this)(modifiers);
this.state.prevModifiers = prevModifiers;
const popperInstanceRef = _reblendjs.useRef.bind(this)(undefined);
this.state.popperInstanceRef = popperInstanceRef;
const update = _reblendjs.useCallback.bind(this)(() => {
this.state.popperInstanceRef.current?.update();
});
this.state.update = update;
const forceUpdate = _reblendjs.useCallback.bind(this)(() => {
this.state.popperInstanceRef.current?.forceUpdate();
});
this.state.forceUpdate = forceUpdate;
const [popperState, setState] = _reblendjs.useState.bind(this)({
placement,
update: this.state.update,
forceUpdate: this.state.forceUpdate,
attributes: {},
styles: {
popper: {},
arrow: {}
}
}, "popperState");
this.state.popperState = popperState;
this.state.setState = setState;
const updateModifier = _reblendjs.useMemo.bind(this)(() => ({
name: 'updateStateModifier',
enabled: true,
phase: 'write',
requires: ['computeStyles'],
fn: ({
state
}) => {
const styles = {};
const attributes = {};
Object.keys(state.elements).forEach(element => {
styles[element] = state.styles[element];
attributes[element] = state.attributes[element];
});
this.state.setState({
state,
styles,
attributes,
update: this.state.update,
forceUpdate: this.state.forceUpdate,
placement: state.placement
});
}
}), "updateModifier", (() => []).bind(this));
this.state.updateModifier = updateModifier;
const nextModifiers = _reblendjs.useMemo.bind(this)(() => {
if (!(0, _dequal.dequal)(this.state.prevModifiers.current, modifiers)) {
this.state.prevModifiers.current = modifiers;
}
return this.state.prevModifiers.current;
}, "nextModifiers", (() => [modifiers]).bind(this));
this.state.nextModifiers = nextModifiers;
_reblendjs.useEffect.bind(this)(() => {
if (!this.state.popperInstanceRef.current || !enabled) return;
this.state.popperInstanceRef.current.setOptions({
placement,
strategy,
modifiers: [...this.state.nextModifiers, this.state.updateModifier, disabledApplyStylesModifier]
});
}, (() => [strategy, placement, this.state.updateModifier, enabled, this.state.nextModifiers]).bind(this));
_reblendjs.useEffect.bind(this)(() => {
if (!enabled || referenceElement == null || popperElement == null) {
return undefined;
}
this.state.popperInstanceRef.current = (0, _popper.createPopper)(referenceElement, popperElement, {
...config,
placement,
strategy,
modifiers: [...this.state.nextModifiers, ariaDescribedByModifier, this.state.updateModifier]
});
return () => {
if (this.state.popperInstanceRef.current != null) {
this.state.popperInstanceRef.current.destroy();
this.state.popperInstanceRef.current = undefined;
this.state.setState(s => ({
...s,
attributes: {},
styles: {
popper: {}
}
}));
}
};
// This is only run once to _create_ the popper
}, (() => [enabled, referenceElement, popperElement]).bind(this));
return this.state.popperState;
}
/* @Reblend: Transformed from function to class */
var _default = exports.default = usePopper;