UNPKG

reblend-ui

Version:

Utilities for creating robust overlay components

165 lines (162 loc) 5.58 kB
"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;