UNPKG

@zag-js/dismissable

Version:

Dismissable layer utilities for the DOM

132 lines (131 loc) 4.33 kB
// src/layer-stack.ts import { contains, nextTick } from "@zag-js/dom-query"; var LAYER_REQUEST_DISMISS_EVENT = "layer:request-dismiss"; var layerStack = { layers: [], branches: [], recentlyRemoved: /* @__PURE__ */ new Set(), count() { return this.layers.length; }, pointerBlockingLayers() { return this.layers.filter((layer) => layer.pointerBlocking); }, topMostPointerBlockingLayer() { return [...this.pointerBlockingLayers()].slice(-1)[0]; }, hasPointerBlockingLayer() { return this.pointerBlockingLayers().length > 0; }, isBelowPointerBlockingLayer(node) { const index = this.indexOf(node); const highestBlockingIndex = this.topMostPointerBlockingLayer() ? this.indexOf(this.topMostPointerBlockingLayer()?.node) : -1; return index < highestBlockingIndex; }, isTopMost(node) { const layer = this.layers[this.count() - 1]; return layer?.node === node; }, getNestedLayers(node) { return Array.from(this.layers).slice(this.indexOf(node) + 1); }, getLayersByType(type) { return this.layers.filter((layer) => layer.type === type); }, getNestedLayersByType(node, type) { const index = this.indexOf(node); if (index === -1) return []; return this.layers.slice(index + 1).filter((layer) => layer.type === type); }, getParentLayerOfType(node, type) { const index = this.indexOf(node); if (index <= 0) return void 0; return this.layers.slice(0, index).reverse().find((layer) => layer.type === type); }, countNestedLayersOfType(node, type) { return this.getNestedLayersByType(node, type).length; }, isInNestedLayer(node, target) { const inNested = this.getNestedLayers(node).some((layer) => contains(layer.node, target)); if (inNested) return true; if (this.recentlyRemoved.size > 0) return true; return false; }, isInBranch(target) { return Array.from(this.branches).some((branch) => contains(branch, target)); }, add(layer) { this.layers.push(layer); this.syncLayers(); }, addBranch(node) { this.branches.push(node); }, remove(node) { const index = this.indexOf(node); if (index < 0) return; this.recentlyRemoved.add(node); nextTick(() => this.recentlyRemoved.delete(node)); if (index < this.count() - 1) { const _layers = this.getNestedLayers(node); _layers.forEach((layer) => layerStack.dismiss(layer.node, node)); } this.layers.splice(index, 1); this.syncLayers(); }, removeBranch(node) { const index = this.branches.indexOf(node); if (index >= 0) this.branches.splice(index, 1); }, syncLayers() { this.layers.forEach((layer, index) => { layer.node.style.setProperty("--layer-index", `${index}`); layer.node.removeAttribute("data-nested"); layer.node.removeAttribute("data-has-nested"); const parentOfSameType = this.getParentLayerOfType(layer.node, layer.type); if (parentOfSameType) { layer.node.setAttribute("data-nested", layer.type); } const nestedCount = this.countNestedLayersOfType(layer.node, layer.type); if (nestedCount > 0) { layer.node.setAttribute("data-has-nested", layer.type); } layer.node.style.setProperty("--nested-layer-count", `${nestedCount}`); }); }, indexOf(node) { return this.layers.findIndex((layer) => layer.node === node); }, dismiss(node, parent) { const index = this.indexOf(node); if (index === -1) return; const layer = this.layers[index]; addListenerOnce(node, LAYER_REQUEST_DISMISS_EVENT, (event) => { layer.requestDismiss?.(event); if (!event.defaultPrevented) { layer?.dismiss(); } }); fireCustomEvent(node, LAYER_REQUEST_DISMISS_EVENT, { originalLayer: node, targetLayer: parent, originalIndex: index, targetIndex: parent ? this.indexOf(parent) : -1 }); this.syncLayers(); }, clear() { this.remove(this.layers[0].node); } }; function fireCustomEvent(el, type, detail) { const win = el.ownerDocument.defaultView || window; const event = new win.CustomEvent(type, { cancelable: true, bubbles: true, detail }); return el.dispatchEvent(event); } function addListenerOnce(el, type, callback) { el.addEventListener(type, callback, { once: true }); } export { layerStack };