zircle
Version:
Zircle's circular components, rebuilt with Orbit and Zumly for the web. No Vue required.
4,376 lines • 206 kB
JavaScript
/*! Zircle, Orbit, and Zumly are MIT licensed. See LICENSES.txt for copyright and permission notices. */
// node_modules/@zumer/orbit/dist/orbit.mjs
var STATE = Symbol.for("zumer.orbit.layout.state.v1");
var REGISTRY = Symbol.for("zumer.orbit.layout.registry.v1");
var PROBE = Symbol.for("zumer.orbit.layout.probe.v1");
var VISUALS = "o-arc, o-progress";
var OBSERVE = { subtree: true, childList: true, characterData: true, attributes: true, attributeOldValue: true };
function orbitNumber(element) {
var _a;
for (const token of element.classList || []) {
if (/^orbit-\d+$/.test(token)) return Number(token.slice(6));
}
return ((_a = element.classList) == null ? void 0 : _a.contains("orbit")) ? null : void 0;
}
function setLayoutProperty(element, name, value) {
const text = String(value);
if (element.style.getPropertyValue(name) !== text) element.style.setProperty(name, text);
}
function cssNumber(element, value, fallback = 0, type = "number") {
const raw = String(value || "").trim();
if (!raw) return fallback;
if (type === "number" && /^[+-]?(?:\d+\.?\d*|\.\d+)$/.test(raw)) return Number(raw);
const angle = raw.match(/^([+-]?(?:\d+\.?\d*|\.\d+))(deg|rad|grad|turn)?$/);
if (type === "angle" && angle) {
return Number(angle[1]) * ({ deg: 1, rad: 180 / Math.PI, grad: 0.9, turn: 360 }[angle[2]] || 1);
}
const doc = element.ownerDocument;
if (!(doc == null ? void 0 : doc.documentElement)) return fallback;
let probe = doc[PROBE];
if (!probe) {
probe = doc.createElement("span");
probe.setAttribute("data-orbit-measure", "");
probe.setAttribute("aria-hidden", "true");
probe.style.cssText = "all:initial!important;position:fixed!important;visibility:hidden!important;pointer-events:none!important;width:0!important;height:0!important;overflow:hidden!important;";
doc.documentElement.appendChild(probe);
Object.defineProperty(doc, PROBE, { value: probe, configurable: true });
}
const property = type === "angle" ? "rotate" : "scale";
probe.style.removeProperty(property);
probe.style.setProperty(property, raw, "important");
if (!probe.style.getPropertyValue(property)) return fallback;
const computed = doc.defaultView.getComputedStyle(probe).getPropertyValue(property);
const resolved = parseFloat(computed);
return Number.isFinite(resolved) ? resolved : fallback;
}
function group(element) {
if (element.localName === "o-arc") return "arc";
for (const name of ["satellite", "vector", "side"]) if (element.classList.contains(name)) return name;
return null;
}
function layoutRing(ring) {
const counts = { arc: 0, satellite: 0, vector: 0, side: 0 };
for (const child of ring.children) {
const kind = group(child);
if (kind) setLayoutProperty(child, "--o-layout-index", counts[kind]++ - (kind === "side" ? 1 : 0));
else if (child.localName === "o-progress") setLayoutProperty(child, "--o-layout-index", 0);
}
const count = Math.max(counts.arc, counts.satellite, counts.vector, 1);
const divisor = counts.side ? String(counts.side) : `max(1, ${count} - var(--o-fit-range, 0))`;
setLayoutProperty(ring, "--o-layout-angle", `calc(var(--o-range, 360deg) / ${divisor})`);
}
function isScope(root) {
return (root == null ? void 0 : root.nodeType) === 9 || (root == null ? void 0 : root.nodeType) === 11 && !!root.host;
}
function scopeFor(element) {
var _a;
if (isScope(element)) return element;
const root = (_a = element == null ? void 0 : element.getRootNode) == null ? void 0 : _a.call(element);
return isScope(root) ? root : null;
}
function composedContains(ancestor, node) {
var _a, _b;
for (let current = node; current; current = (_a = current.getRootNode) == null ? void 0 : _a.call(current).host) {
if (ancestor === current || ((_b = ancestor.contains) == null ? void 0 : _b.call(ancestor, current))) return true;
}
return false;
}
function registryFor(doc) {
if (!doc[REGISTRY]) Object.defineProperty(doc, REGISTRY, {
value: { states: /* @__PURE__ */ new Set() },
configurable: true
});
return doc[REGISTRY];
}
function dispose(state) {
if (state.disposed) return;
state.disposed = true;
state.view.cancelAnimationFrame(state.frame);
state.observer.disconnect();
state.resize.disconnect();
for (const remove of state.listeners) remove();
state.rings.clear();
state.visuals.clear();
state.dirty.clear();
state.registry.states.delete(state);
if (state.scope[STATE] === state) delete state.scope[STATE];
}
function pruneDisconnected(registry) {
for (const state of registry.states) {
if (state.scope.host && !state.scope.host.isConnected) dispose(state);
}
}
function invalidateDescendants(state, target = state.scope) {
for (const other of state.registry.states) {
if (other !== state && other.scope.host && composedContains(target, other.scope.host)) {
other.all = true;
other.queue();
}
}
}
function hasLayoutStructure(node) {
if (node.nodeType !== 1 || node.hasAttribute("data-orbit-measure")) return false;
if (orbitNumber(node) !== void 0 || node.matches(".gravity-spot, o-arc, o-progress")) return true;
return !!node.querySelector(".gravity-spot, .orbit, [data-orbit-ring], o-arc, o-progress");
}
function hasStylesheet(node) {
return node.nodeType === 1 && (node.matches('style, link[rel~="stylesheet"]') || !!node.querySelector('style, link[rel~="stylesheet"]'));
}
function needsDiscovery(record, state) {
if (record.type === "childList") {
return [...record.addedNodes, ...record.removedNodes].some(hasLayoutStructure);
}
if (record.attributeName !== "class") return false;
const element = record.target;
return state.rings.has(element) || element.classList.contains("gravity-spot") || orbitNumber(element) !== void 0 || (record.oldValue || "").split(/\s+/).some((token) => token === "gravity-spot" || token === "orbit" || /^orbit-\d+$/.test(token));
}
function createState(scope) {
const doc = scope.nodeType === 9 ? scope : scope.ownerDocument;
const view = doc.defaultView;
const registry = registryFor(doc);
const state = {
scope,
doc,
view,
registry,
rings: /* @__PURE__ */ new Set(),
visuals: /* @__PURE__ */ new Set(),
dirty: /* @__PURE__ */ new Set(),
scan: true,
all: true,
frame: 0,
disposed: false,
listeners: []
};
const listen = (target, event, listener, options) => {
target == null ? void 0 : target.addEventListener(event, listener, options);
state.listeners.push(() => target == null ? void 0 : target.removeEventListener(event, listener, options));
};
const queue = () => {
if (!state.disposed && !state.frame) state.frame = view.requestAnimationFrame(() => {
state.frame = 0;
state.flush();
});
};
state.queue = queue;
state.flush = () => flush(state);
state.invalidate = (target = scope, includeDescendants = true) => {
var _a, _b;
if (includeDescendants && (target === scope || target === doc.documentElement || target === doc.head)) state.all = true;
else {
for (const ring of state.rings) if (ring === target || ring.contains(target) || includeDescendants && ((_a = target.contains) == null ? void 0 : _a.call(target, ring))) state.dirty.add(ring);
for (const visual of state.visuals) if (visual === target || visual.contains(target) || includeDescendants && ((_b = target.contains) == null ? void 0 : _b.call(target, visual))) state.dirty.add(visual);
}
if (state.all || state.dirty.size || state.scan) queue();
};
state.observer = new view.MutationObserver((records) => {
var _a, _b;
if (records.some((record) => record.type === "childList")) pruneDisconnected(registry);
if (state.disposed) return;
for (const record of records) {
const target = record.target.nodeType === 3 ? record.target.parentElement : record.target;
if (!target || ((_a = target.hasAttribute) == null ? void 0 : _a.call(target, "data-orbit-measure"))) continue;
const structural = needsDiscovery(record, state);
if (structural) state.scan = true;
const stylesheetChange = record.type === "childList" && [...record.addedNodes, ...record.removedNodes].some(hasStylesheet);
if (stylesheetChange || ((_b = target.closest) == null ? void 0 : _b.call(target, "head, style")) || target.localName === "link") {
state.all = true;
invalidateDescendants(state);
} else if (record.type === "attributes") invalidateDescendants(state, target);
state.invalidate(target, record.type === "attributes" || structural);
}
});
state.observer.observe(scope, OBSERVE);
state.resize = new view.ResizeObserver((entries) => {
for (const entry of entries) state.dirty.add(entry.target);
queue();
});
const invalidateAll = () => {
state.all = true;
invalidateDescendants(state);
queue();
};
if (scope === doc) {
listen(view, "resize", invalidateAll);
listen(doc.fonts, "loadingdone", invalidateAll);
}
listen(scope, "load", (event) => {
if (event.target.localName === "link") invalidateAll();
}, true);
for (const event of ["pointerover", "pointerout", "focusin", "focusout"]) {
listen(scope, event, (e) => {
state.invalidate(e.target);
invalidateDescendants(state, e.target);
}, true);
}
Object.defineProperty(scope, STATE, { value: state, configurable: true });
registry.states.add(state);
queue();
return state;
}
function ensureState(scope) {
const doc = scope.nodeType === 9 ? scope : scope.ownerDocument;
if (!(doc == null ? void 0 : doc.defaultView) || !doc.documentElement || scope.host && !scope.host.isConnected) return null;
if (scope[STATE] && scope[STATE].doc !== doc) dispose(scope[STATE]);
if (scope.host) {
const parent = scopeFor(scope.host);
if (parent) ensureState(parent);
}
return scope[STATE] || createState(scope);
}
function discover(state) {
const nextRings = /* @__PURE__ */ new Set();
const automaticCounts = /* @__PURE__ */ new Map();
for (const ring of state.scope.querySelectorAll(".gravity-spot > *")) {
const number = orbitNumber(ring);
if (number === void 0) continue;
let automatic = automaticCounts.get(ring.parentElement) || 0;
if (ring.classList.contains("orbit")) automaticCounts.set(ring.parentElement, ++automatic);
const resolved = number === null ? automatic : number;
ring.setAttribute("data-orbit-ring", "");
setLayoutProperty(ring, "--o-layout-number", resolved === 0 ? 1e-5 : resolved);
nextRings.add(ring);
state.dirty.add(ring);
if (!state.rings.has(ring)) state.resize.observe(ring);
}
for (const old of state.rings) if (!nextRings.has(old)) {
state.resize.unobserve(old);
old.removeAttribute("data-orbit-ring");
for (const name of ["--o-layout-number", "--o-layout-angle"]) old.style.removeProperty(name);
}
state.rings = nextRings;
const nextVisuals = new Set(state.scope.querySelectorAll(VISUALS));
for (const old of state.visuals) if (!nextVisuals.has(old)) state.resize.unobserve(old);
for (const visual of nextVisuals) if (!state.visuals.has(visual)) {
state.resize.observe(visual);
state.dirty.add(visual);
}
state.visuals = nextVisuals;
state.scan = false;
}
function flush(state) {
var _a;
if (state.disposed) return;
if (state.scope.host && !state.scope.host.isConnected) {
dispose(state);
return;
}
state.observer.disconnect();
try {
if (state.scan) discover(state);
const dirty = state.all ? /* @__PURE__ */ new Set([...state.rings, ...state.visuals]) : state.dirty;
const visuals = /* @__PURE__ */ new Set();
for (const target of dirty) {
if (!target.isConnected) continue;
if (state.rings.has(target)) {
layoutRing(target);
for (const visual of target.querySelectorAll(VISUALS)) visuals.add(visual);
} else if (state.visuals.has(target)) visuals.add(target);
}
for (const visual of state.visuals) if (visuals.has(visual)) (_a = visual.update) == null ? void 0 : _a.call(visual);
state.dirty = /* @__PURE__ */ new Set();
state.all = false;
} finally {
if (!state.disposed) state.observer.observe(state.scope, OBSERVE);
}
}
function requestLayout(element = globalThis.document) {
var _a;
const scope = scopeFor(element);
if (!scope) return;
const state = ensureState(scope);
if (!state) return;
if (((_a = element == null ? void 0 : element.matches) == null ? void 0 : _a.call(element, VISUALS)) && !state.visuals.has(element)) state.scan = true;
state.invalidate(element || scope);
}
function refreshLayout(root = globalThis.document) {
const scope = scopeFor(root);
if (!scope) return;
const state = ensureState(scope);
if (!state) return;
pruneDisconnected(state.registry);
for (const current of state.registry.states) {
if (current !== state && (!current.scope.host || !composedContains(root, current.scope.host))) continue;
current.scan = true;
current.all = true;
current.view.cancelAnimationFrame(current.frame);
current.frame = 0;
current.flush();
}
}
var OrbitBase = class extends (globalThis.HTMLElement || class {
}) {
constructor() {
super();
}
connectedCallback() {
requestLayout(this);
}
disconnectedCallback() {
if (this._textFrame) this.ownerDocument.defaultView.cancelAnimationFrame(this._textFrame);
this._textFrame = 0;
}
readNumber(style, name, fallback = 0) {
return cssNumber(this, style.getPropertyValue(name), fallback);
}
readAngle(style, name, fallback = 0) {
return cssNumber(this, style.getPropertyValue(name), fallback, "angle");
}
getCommonAttributes(element) {
var _a;
const style = element.ownerDocument.defaultView.getComputedStyle(element);
const measuredRadius = parseFloat(style.getPropertyValue("r"));
const orbitRadius = Math.max(0, Number.isFinite(measuredRadius) ? measuredRadius : (((_a = element.parentElement) == null ? void 0 : _a.clientWidth) || 0) / 2);
const orbitNumber2 = Math.max(1e-5, this.readNumber(style, "--o-orbit-number", 1));
const size = Math.max(0, this.readNumber(style, "--o-size-ratio", 1));
const strokeWidth = Math.max(0, this.readNumber(style, "--o-stroke-width", 1));
const shape = element.getAttribute("shape") || "none";
const arcHeight = Math.max(0, orbitRadius / orbitNumber2 * size - strokeWidth + 0.3);
const arcHeightPercentage = orbitRadius > 0 ? Math.min(49.999, arcHeight * 25 / orbitRadius) : 0;
let innerOuter = 0;
if (element.classList.contains("outer-orbit")) {
innerOuter = arcHeightPercentage;
} else if (element.classList.contains("quarter-outer-orbit")) {
innerOuter = arcHeightPercentage * -0.5;
} else if (element.classList.contains("inner-orbit")) {
innerOuter = arcHeightPercentage * -1;
} else if (element.classList.contains("quarter-inner-orbit")) {
innerOuter = arcHeightPercentage * 0.5;
}
const realRadius = 50 + innerOuter;
return {
orbitRadius,
arcHeight,
realRadius,
arcAngle: 0,
// Se sobrescribe en cada componente
shape,
arcHeightPercentage,
orbitNumber: orbitNumber2,
size,
strokeWidth,
style
};
}
getProgressAngle(maxAngle, value, maxValue = 100) {
if (!Number.isFinite(value) || !Number.isFinite(maxValue) || maxValue <= 0) return 0;
return Math.min(1, Math.max(0, value / maxValue)) * Math.max(0, Math.min(360, maxAngle));
}
getControlPoint(x, y, x1, y1, direction = "clockwise") {
const xm = (x + x1) / 2;
const ym = (y + y1) / 2;
const dx = x1 - x;
const dy = y1 - y;
if (direction === "clockwise") {
return {
xc: xm + dy * 0.4,
yc: ym - dx * 0.4
};
}
return {
xc: xm - dy * 0.4,
yc: ym + dx * 0.4
};
}
arcPoint(radius, angle, radiusAdjustment = 0, angleOffsetDegrees = 0) {
const adjustedRadius = radius + radiusAdjustment;
const adjustedAngle = angle + angleOffsetDegrees * Math.PI / 180;
return {
x: 50 + adjustedRadius * Math.cos(adjustedAngle),
y: 50 + adjustedRadius * Math.sin(adjustedAngle)
};
}
calculateCommonArcParameters(arcAngle, radius, arcHeightPercentage, orbitNumber2, shape, strokeWidth, arcHeight, gap = 0) {
const offset = Math.PI / 2;
const fangle = Math.max(0, Math.min(359.999999, arcAngle)) * Math.PI / 180;
const bigRadius = radius + arcHeightPercentage;
const smallRadius = Math.max(1e-3, radius - arcHeightPercentage);
const bigGap = Math.min(fangle * 0.49, (gap + strokeWidth * 1.25) / orbitNumber2 / bigRadius);
const smallGap = Math.min(fangle * 0.49, (gap + strokeWidth * 1.25) / orbitNumber2 / smallRadius);
const upperAngleStart = bigGap - offset;
const upperAngleEnd = fangle - bigGap - offset;
const innerAngleStart = smallGap - offset;
const innerAngleEnd = fangle - smallGap - offset;
const upperArcStart = this.arcPoint(bigRadius, upperAngleStart);
const upperArcEnd = this.arcPoint(bigRadius, upperAngleEnd);
const innerArcStart = this.arcPoint(smallRadius, innerAngleStart);
const innerArcEnd = this.arcPoint(smallRadius, innerAngleEnd);
const upperSweep = upperAngleEnd - upperAngleStart;
const innerSweep = innerAngleEnd - innerAngleStart;
const largeArcFlagUpper = upperSweep > Math.PI ? 1 : 0;
const largeArcFlagInner = innerSweep > Math.PI ? 1 : 0;
return {
upperArcStart,
upperArcEnd,
innerArcStart,
innerArcEnd,
largeArcFlag: largeArcFlagUpper,
// back-compat alias; prefer the two below
largeArcFlagUpper,
largeArcFlagInner,
bigRadius,
smallRadius,
radius,
upperAngleStart,
upperAngleEnd,
innerAngleStart,
innerAngleEnd
};
}
generatePathData(shape, params, arcHeight, orbitNumber2) {
let d = "";
switch (shape) {
case "rounded":
d = this.generateRoundedPath(params, arcHeight, orbitNumber2);
break;
case "circle":
case "circle-a":
case "bullet":
d = this.generateCirclePath(params, shape);
break;
case "circle-b":
d = this.generateCircleBPath(params, arcHeight, orbitNumber2);
break;
case "arrow":
d = this.generateArrowPath(params, orbitNumber2);
break;
case "backslash":
case "slash":
d = this.generateSlashPath(params, shape, orbitNumber2);
break;
case "zigzag":
d = this.generateZigzagPath(params, arcHeight, orbitNumber2);
break;
default:
d = this.generateDefaultPath(params);
}
return d;
}
generateRoundedPath(params, arcHeight, orbitNumber2) {
const { bigRadius, smallRadius } = params;
const available = Math.min(params.upperAngleEnd - params.upperAngleStart, params.innerAngleEnd - params.innerAngleStart);
const curve = Math.min(arcHeight < 5 ? 2.5 : arcHeight < 10 ? 5 : 10, available * 180 / Math.PI * orbitNumber2 * 0.49);
const capRad = curve / orbitNumber2 * Math.PI / 180;
const flagU = params.upperAngleEnd - params.upperAngleStart - 2 * capRad > Math.PI ? 1 : 0;
const flagI = params.innerAngleEnd - params.innerAngleStart - 2 * capRad > Math.PI ? 1 : 0;
const newUpperStart = this.arcPoint(bigRadius, params.upperAngleStart, 0, curve / orbitNumber2);
const newUpperEnd = this.arcPoint(bigRadius, params.upperAngleEnd, 0, -curve / orbitNumber2);
const newInnerStart = this.arcPoint(smallRadius, params.innerAngleStart, 0, curve / orbitNumber2);
const newInnerEnd = this.arcPoint(smallRadius, params.innerAngleEnd, 0, -curve / orbitNumber2);
const upperPointStart = this.arcPoint(bigRadius, params.upperAngleStart, -(curve / 2) / orbitNumber2, 0);
const upperPointEnd = this.arcPoint(bigRadius, params.upperAngleEnd, -(curve / 2) / orbitNumber2, 0);
const innerPointStart = this.arcPoint(smallRadius, params.innerAngleStart, curve / 2 / orbitNumber2, 0);
const innerPointEnd = this.arcPoint(smallRadius, params.innerAngleEnd, curve / 2 / orbitNumber2, 0);
const Q2 = this.getControlPoint(newUpperEnd.x, newUpperEnd.y, upperPointEnd.x, upperPointEnd.y);
const Q1 = this.getControlPoint(innerPointEnd.x, innerPointEnd.y, newInnerEnd.x, newInnerEnd.y);
const Q22 = this.getControlPoint(newInnerStart.x, newInnerStart.y, innerPointStart.x, innerPointStart.y);
const Q3 = this.getControlPoint(upperPointStart.x, upperPointStart.y, newUpperStart.x, newUpperStart.y);
let d = `M ${newUpperStart.x},${newUpperStart.y} A ${bigRadius},${bigRadius} 0 ${flagU} 1 ${newUpperEnd.x},${newUpperEnd.y}`;
d += `Q ${Q2.xc},${Q2.yc} ${upperPointEnd.x},${upperPointEnd.y} L ${innerPointEnd.x},${innerPointEnd.y}`;
d += `Q ${Q1.xc},${Q1.yc} ${newInnerEnd.x},${newInnerEnd.y}`;
d += `A ${smallRadius},${smallRadius} 0 ${flagI} 0 ${newInnerStart.x},${newInnerStart.y}`;
d += `Q ${Q22.xc},${Q22.yc} ${innerPointStart.x},${innerPointStart.y} L ${upperPointStart.x},${upperPointStart.y}`;
d += ` Q ${Q3.xc},${Q3.yc} ${newUpperStart.x},${newUpperStart.y}`;
d += ` Z`;
return d;
}
// Dentro de la clase OrbitCommon en orbit-common.js
generateCirclePath(params, shape) {
const { upperArcStart, upperArcEnd, innerArcStart, innerArcEnd, bigRadius, smallRadius, largeArcFlagUpper, largeArcFlagInner } = params;
let d = `M ${upperArcStart.x},${upperArcStart.y} A ${bigRadius},${bigRadius} 0 ${largeArcFlagUpper} 1 ${upperArcEnd.x},${upperArcEnd.y}`;
d += ` A 1,1 0 0 1 ${innerArcEnd.x},${innerArcEnd.y} `;
d += ` A ${smallRadius},${smallRadius} 0 ${largeArcFlagInner} 0 ${innerArcStart.x},${innerArcStart.y}`;
d += ` A 1,1 0 0 ${shape === "circle" || shape === "circle-a" ? 1 : 0} ${upperArcStart.x},${upperArcStart.y} `;
d += ` Z`;
return d;
}
generateCircleBPath(params, arcHeight, orbitNumber2) {
const { upperAngleStart, upperAngleEnd, innerAngleStart, innerAngleEnd, bigRadius, smallRadius } = params;
const available = Math.min(upperAngleEnd - upperAngleStart, innerAngleEnd - innerAngleStart);
const segment = Math.min(arcHeight * 1.36, available * 180 / Math.PI * orbitNumber2 * 0.49);
const capRad = segment / orbitNumber2 * Math.PI / 180;
const flagU = upperAngleEnd - upperAngleStart - 2 * capRad > Math.PI ? 1 : 0;
const flagI = innerAngleEnd - innerAngleStart - 2 * capRad > Math.PI ? 1 : 0;
const newUpperStart = this.arcPoint(bigRadius, upperAngleStart, 0, segment / orbitNumber2);
const newUpperEnd = this.arcPoint(bigRadius, upperAngleEnd, 0, -segment / orbitNumber2);
const newInnerStart = this.arcPoint(smallRadius, innerAngleStart, 0, segment / orbitNumber2);
const newInnerEnd = this.arcPoint(smallRadius, innerAngleEnd, 0, -segment / orbitNumber2);
let d = `M ${newUpperStart.x},${newUpperStart.y} A ${bigRadius},${bigRadius} 0 ${flagU} 1 ${newUpperEnd.x},${newUpperEnd.y}`;
d += ` A 1,1 0 0 1 ${newInnerEnd.x},${newInnerEnd.y} `;
d += ` A ${smallRadius},${smallRadius} 0 ${flagI} 0 ${newInnerStart.x},${newInnerStart.y}`;
d += ` A 1,1 0 0 1 ${newUpperStart.x},${newUpperStart.y} `;
d += ` Z`;
return d;
}
generateArrowPath(params, orbitNumber2) {
const { upperArcStart, upperArcEnd, innerArcStart, innerArcEnd, bigRadius, smallRadius, largeArcFlagUpper, largeArcFlagInner, radius } = params;
const middleEnd = this.arcPoint(radius, params.upperAngleEnd, 0, 24 / orbitNumber2 / 2);
const middleStart = this.arcPoint(radius, params.upperAngleStart, 0, 24 / orbitNumber2 / 2);
let d = `M ${upperArcStart.x},${upperArcStart.y} A ${bigRadius},${bigRadius} 0 ${largeArcFlagUpper} 1 ${upperArcEnd.x},${upperArcEnd.y}`;
d += `L ${middleEnd.x} ${middleEnd.y}`;
d += `L ${innerArcEnd.x} ${innerArcEnd.y}`;
d += `A ${smallRadius},${smallRadius} 0 ${largeArcFlagInner} 0 ${innerArcStart.x}, ${innerArcStart.y}`;
d += `L ${middleStart.x} ${middleStart.y}`;
d += `Z`;
return d;
}
generateSlashPath(params, shape, orbitNumber2) {
const { upperAngleStart, upperAngleEnd, innerAngleStart, innerAngleEnd, bigRadius, smallRadius, largeArcFlagUpper, largeArcFlagInner } = params;
const newUpperStart = this.arcPoint(bigRadius, upperAngleStart, 0, shape === "backslash" ? 0 : 24 / orbitNumber2 / 2);
const newUpperEnd = this.arcPoint(bigRadius, upperAngleEnd, 0, shape === "backslash" ? 0 : 24 / orbitNumber2 / 2);
const newInnerStart = this.arcPoint(smallRadius, innerAngleStart, 0, shape === "backslash" ? 24 / orbitNumber2 / 2 : 0);
const newInnerEnd = this.arcPoint(smallRadius, innerAngleEnd, 0, shape === "backslash" ? 24 / orbitNumber2 / 2 : 0);
let d = `M ${newUpperStart.x},${newUpperStart.y} A ${bigRadius},${bigRadius} 0 ${largeArcFlagUpper} 1 ${newUpperEnd.x},${newUpperEnd.y}`;
d += `L ${newInnerEnd.x} ${newInnerEnd.y}`;
d += `A ${smallRadius},${smallRadius} 0 ${largeArcFlagInner} 0 ${newInnerStart.x}, ${newInnerStart.y}`;
d += `Z`;
return d;
}
generateZigzagPath(params, arcHeight, orbitNumber2) {
const { upperArcStart, upperArcEnd, innerArcStart, innerArcEnd, bigRadius, smallRadius, largeArcFlagUpper, largeArcFlagInner, radius } = params;
const h2 = arcHeight / orbitNumber2 / 2;
const s2 = this.arcPoint(radius, params.upperAngleStart, -h2, 3);
const s3 = this.arcPoint(radius, params.upperAngleStart, 0, 0);
const s4 = this.arcPoint(radius, params.upperAngleStart, h2, 3);
const e2 = this.arcPoint(radius, params.innerAngleEnd, h2, 3);
const e3 = this.arcPoint(radius, params.innerAngleEnd, 0, 0);
const e4 = this.arcPoint(radius, params.innerAngleEnd, -h2, 3);
let d = `M ${upperArcStart.x},${upperArcStart.y} A ${bigRadius},${bigRadius} 0 ${largeArcFlagUpper} 1 ${upperArcEnd.x},${upperArcEnd.y}`;
d += `L ${e2.x} ${e2.y}`;
d += `L ${e3.x} ${e3.y}`;
d += `L ${e4.x} ${e4.y}`;
d += `L ${innerArcEnd.x} ${innerArcEnd.y}`;
d += `A ${smallRadius},${smallRadius} 0 ${largeArcFlagInner} 0 ${innerArcStart.x}, ${innerArcStart.y}`;
d += `L ${s2.x} ${s2.y}`;
d += `L ${s3.x} ${s3.y}`;
d += `L ${s4.x} ${s4.y}`;
d += `Z`;
return d;
}
generateDefaultPath(params) {
const { upperArcStart, upperArcEnd, innerArcStart, innerArcEnd, bigRadius, smallRadius, largeArcFlagUpper, largeArcFlagInner } = params;
let d = `M ${upperArcStart.x},${upperArcStart.y} A ${bigRadius},${bigRadius} 0 ${largeArcFlagUpper} 1 ${upperArcEnd.x},${upperArcEnd.y}`;
d += `L ${innerArcEnd.x} ${innerArcEnd.y}`;
d += `A ${smallRadius},${smallRadius} 0 ${largeArcFlagInner} 0 ${innerArcStart.x}, ${innerArcStart.y}`;
d += `Z`;
return d;
}
};
var OrbitProgress = class extends OrbitBase {
constructor() {
super();
this.attachShadow({ mode: "open" });
this.shadowRoot.innerHTML = `
<style>
:host {
--o-fill: var(--o-gray-light);
--o-stroke: var(--o-fill);
--o-stroke-width: 1;
--o-back-fill: transparent;
--o-back-stroke: none;
--o-back-stroke-width: 1;
}
:host(:hover) {
--o-fill: var(--o-gray-light);
--o-stroke: var(--o-fill);
--o-stroke-width: 1;
--o-back-fill: transparent;
--o-back-stroke: none;
--o-back-stroke-width: 1;
}
svg {
width: 100%;
height: 100%;
overflow: visible;
pointer-events: none;
}
/* Display element by default: never steals clicks (see orbit-arc).
Interaction is opt-in via the "interactive" attribute. */
svg * {
pointer-events: none;
}
:host([interactive]) svg * {
pointer-events: visiblePainted;
}
:host([interactive]) {
cursor: pointer;
}
.progress-bar {
fill: var(--o-fill);
stroke: var(--o-stroke);
stroke-width: var(--o-stroke-width);
transition: fill 0.25s, stroke 0.25s;
stroke-linejoin: round;
}
.progress-bg {
fill: var(--o-back-fill);
stroke: var(--o-back-stroke);
stroke-width: var(--o-back-stroke-width);
}
/* variant="stroke": the thin, legible gauge (registro "medidor").
The default band is a FILLED donut wedge, so everyone building a
thin gauge tripped on --o-fill/--o-back-fill. With this variant the
paths are open arcs and the data color lives where you expect it:
--o-stroke for the bar, --o-back-stroke for the track. */
:host([variant="stroke"]) .progress-bar {
fill: none;
stroke: var(--o-stroke);
stroke-width: var(--o-stroke-width, 2);
stroke-linecap: round;
}
:host([variant="stroke"]) .progress-bg {
fill: none;
stroke: var(--o-back-stroke, var(--o-gray-light));
stroke-width: var(--o-back-stroke-width, 1);
stroke-linecap: round;
}
</style>
<svg viewBox="0 0 100 100">
<path class="progress-bg" shape-rendering="geometricPrecision" vector-effect="non-scaling-stroke"></path>
<path class="progress-bar" shape-rendering="geometricPrecision" vector-effect="non-scaling-stroke"></path>
</svg>
`;
}
update() {
const attrs = this.getAttributes();
const isStroke = this.getAttribute("variant") === "stroke";
const dBg = isStroke ? this.calculateStrokeArc(attrs, true) : this.calculateArcParameters(attrs, true);
const dBar = isStroke ? this.calculateStrokeArc(attrs, false) : this.calculateArcParameters(attrs, false);
this.shadowRoot.querySelector(".progress-bg").setAttribute("d", dBg);
this.shadowRoot.querySelector(".progress-bar").setAttribute("d", dBar);
}
/**
* variant="stroke": open arc along the orbit radius (no closed band),
* stroked by CSS. Same progress math as the band variant.
*/
calculateStrokeArc(attrs, full) {
const { realRadius } = attrs;
const arcAngle = Math.max(0, Math.min(this.getProgressAngle(attrs, full), 359.999999));
if (!(arcAngle > 0) || !(attrs.orbitRadius > 0)) return "";
const a0 = -90 * (Math.PI / 180);
const a1 = (-90 + arcAngle) * (Math.PI / 180);
const x0 = 50 + realRadius * Math.cos(a0);
const y0 = 50 + realRadius * Math.sin(a0);
const x1 = 50 + realRadius * Math.cos(a1);
const y1 = 50 + realRadius * Math.sin(a1);
const largeArcFlag = arcAngle > 180 ? 1 : 0;
return `M ${x0},${y0} A ${realRadius},${realRadius} 0 ${largeArcFlag} 1 ${x1},${y1}`;
}
getAttributes() {
const common = super.getCommonAttributes(this);
const range = Math.max(0, Math.min(360, this.readAngle(common.style, "--o-range", 360)));
const rawValue = common.style.getPropertyValue("--o-progress").trim();
const progress = rawValue ? this.readNumber(common.style, "--o-progress", 0) : Number(this.getAttribute("value") || 0);
const rawMax = this.getAttribute("max");
const maxValue = rawMax === null ? 100 : Number(rawMax);
return {
...common,
range,
progress,
maxValue
};
}
getProgressAngle(attrs, full) {
const { range, progress, maxValue } = attrs;
return full ? range : super.getProgressAngle(range, progress, maxValue);
}
calculateArcParameters(attrs, full) {
const { shape, realRadius, arcHeightPercentage, orbitNumber: orbitNumber2, strokeWidth, arcHeight } = attrs;
const arcAngle = this.getProgressAngle(attrs, full);
if (!(arcAngle > 0) || !(attrs.orbitRadius > 0)) return "";
const params = super.calculateCommonArcParameters(
arcAngle,
realRadius,
arcHeightPercentage,
orbitNumber2,
shape,
strokeWidth,
arcHeight
);
return super.generatePathData(shape, params, arcHeight, orbitNumber2);
}
};
var template = typeof document === "undefined" ? null : document.createElement("template");
if (template) template.innerHTML = `
<style>
:host {
--o-fill: var(--o-gray-light);
--o-stroke: var(--o-fill);
--o-stroke-width: 1;
--o-color: currentcolor;
}
:host(:hover) {
--o-fill: var(--o-gray-light);
--o-stroke: var(--o-fill);
--o-stroke-width: 1;
--o-color: currentcolor;
}
svg {
width: 100%;
height: 100%;
overflow: visible;
pointer-events: none;
}
/* Arcs are display elements by default: they must NOT steal clicks from
satellites/controls nearby (a painted arc used to capture clicks even
at opacity 0, and pointer-events:none on the host could not pierce the
shadow). Interaction is opt-in via the "interactive" attribute. */
svg * {
pointer-events: none;
}
:host([interactive]) svg * {
pointer-events: visiblePainted;
}
:host([interactive]) {
cursor: pointer;
}
#orbitShape {
fill: var(--o-fill);
stroke: var(--o-stroke);
stroke-width: var(--o-stroke-width);
transition: fill 0.25s, stroke 0.25s;
}
text {
fill: var(--o-color);
}
#orbitPath {
fill: transparent;
stroke: none;
stroke-width: 0;
}
</style>
<svg viewBox="0 0 100 100">
<path id="orbitShape" shape-rendering="geometricPrecision" vector-effect="non-scaling-stroke"></path>
<path id="orbitPath" shape-rendering="geometricPrecision" vector-effect="non-scaling-stroke"></path>
<text>
<textPath href="#orbitPath" alignment-baseline="middle"></textPath>
</text>
</svg>
`;
var OrbitArc = class extends OrbitBase {
constructor() {
super();
this.attachShadow({ mode: "open" });
this.shadowRoot.appendChild(template.content.cloneNode(true));
}
update() {
const attrs = this.getAttributes();
const { length, fontSize, textAnchor, fitRange } = attrs;
if (this.hasAttribute("value")) {
setLayoutProperty(this, "--o-arc-start", `${attrs.stackOffset}deg`);
setLayoutProperty(this, "--o_stack", attrs.stackOffset + attrs.arcAngle);
} else {
this.style.removeProperty("--o-arc-start");
this.style.removeProperty("--o_stack");
}
const orbitPath = this.shadowRoot.querySelector("#orbitPath");
const orbitShape = this.shadowRoot.querySelector("#orbitShape");
const textPath = this.shadowRoot.querySelector("textPath");
orbitShape.setAttribute("d", this.calculateArcParameters(attrs).dShape);
orbitPath.setAttribute("d", this.calculateTextArcParameters(attrs).dPath);
if (textAnchor === "start") {
textPath.setAttribute("startOffset", "0%");
textPath.setAttribute("text-anchor", "start");
} else if (textAnchor === "middle") {
textPath.setAttribute("startOffset", "50%");
textPath.setAttribute("text-anchor", "middle");
} else if (textAnchor === "end") {
textPath.setAttribute("startOffset", "100%");
textPath.setAttribute("text-anchor", "end");
}
if (fitRange) {
textPath.parentElement.setAttribute("textLength", orbitPath.getTotalLength());
} else {
textPath.parentElement.removeAttribute("textLength");
}
textPath.parentElement.style.fontSize = `calc(${fontSize} * (100 / (${length}) * (12 / var(--o-orbit-number)))`;
textPath.textContent = this.textContent;
this.warnIfTextOverflows(orbitPath, textPath, fitRange);
}
/**
* Curved text has an explicit angular budget: glyphs past the end of the
* arc path are clipped SILENTLY by SVG. Measure and warn so nobody loses
* time to invisible truncation (~20 chars in 96° at default sizes).
* fit-range squeezes the text to the path via textLength, so it never clips.
*/
warnIfTextOverflows(orbitPath, textPath, fitRange) {
const raw = (this.textContent || "").trim();
if (!raw || fitRange || !this.isConnected) return;
if (this._textFrame) this.ownerDocument.defaultView.cancelAnimationFrame(this._textFrame);
this._textFrame = this.ownerDocument.defaultView.requestAnimationFrame(() => {
this._textFrame = 0;
if (!this.isConnected) return;
try {
const pathLen = orbitPath.getTotalLength();
const textLen = textPath.parentElement.getComputedTextLength();
if (pathLen > 0 && textLen > pathLen && this._truncWarned !== raw) {
this._truncWarned = raw;
console.warn(
`[orbit] <o-arc> text "${raw.length > 34 ? raw.slice(0, 34) + "\u2026" : raw}" overflows its arc (${Math.round(textLen)} > ${Math.round(pathLen)} units) and will clip. Shorten the text, widen --o-range, or use fit-range to squeeze it.`
);
}
} catch (e) {
}
});
}
getAttributes() {
const common = super.getCommonAttributes(this);
const { style } = common;
const range = Math.max(0, Math.min(360, this.readAngle(style, "--o-range", 360)));
const flip = this.hasAttribute("flip") || this.classList.contains("flip");
const fitRange = this.hasAttribute("fit-range") || this.classList.contains("fit-range");
const length = common.orbitRadius * 24 / common.orbitNumber || 100;
const textAnchor = this.getAttribute("text-anchor") || "middle";
const fontSize = style.fontSize || "16px";
const gap = Math.max(0, this.readNumber(style, "--o-gap", 1));
const value = Number(this.getAttribute("value"));
const rawMax = this.getAttribute("max");
const max = rawMax === null ? 100 : Number(rawMax);
const arcAngle = this.hasAttribute("value") ? super.getProgressAngle(range, value, max) : Math.max(0, Math.min(360, this.readAngle(style, "--o-angle", 0)));
let stackOffset = 0;
if (this.hasAttribute("value")) {
for (let prev = this.previousElementSibling; prev; prev = prev.previousElementSibling) {
if (prev.localName !== "o-arc") continue;
const previousStyle = this.ownerDocument.defaultView.getComputedStyle(prev);
if (prev.hasAttribute("value")) {
const prevMax = prev.hasAttribute("max") ? Number(prev.getAttribute("max")) : 100;
const prevRange = cssNumber(prev, previousStyle.getPropertyValue("--o-range"), range, "angle");
stackOffset += super.getProgressAngle(prevRange, Number(prev.getAttribute("value")), prevMax);
} else stackOffset += cssNumber(prev, previousStyle.getPropertyValue("--o-angle"), 0, "angle");
}
}
return { ...common, gap, arcAngle, stackOffset, flip, fitRange, length, fontSize, textAnchor };
}
calculateArcParameters(attrs) {
const { arcAngle, realRadius, arcHeightPercentage, orbitNumber: orbitNumber2, shape, strokeWidth, arcHeight, gap } = attrs;
if (!(arcAngle > 0) || !(attrs.orbitRadius > 0)) return { dShape: "" };
const params = super.calculateCommonArcParameters(
arcAngle,
realRadius,
arcHeightPercentage,
orbitNumber2,
shape,
strokeWidth,
arcHeight,
gap
);
const dShape = super.generatePathData(shape, params, arcHeight, orbitNumber2);
return { dShape };
}
calculateTextArcParameters(attrs) {
const { arcAngle, realRadius, gap, flip } = attrs;
if (!(arcAngle > 0)) return { dPath: "" };
const adjustedGap = Math.min(gap * 0.5, arcAngle * 0.49);
const sweepFlag = flip ? 0 : 1;
const largeArcFlag = arcAngle <= 180 ? 0 : 1;
let coordX1 = 50 + realRadius * Math.cos((-90 + adjustedGap) * (Math.PI / 180));
let coordY1 = 50 + realRadius * Math.sin((-90 + adjustedGap) * (Math.PI / 180));
let coordX2 = 50 + realRadius * Math.cos((arcAngle - 90 - adjustedGap) * Math.PI / 180);
let coordY2 = 50 + realRadius * Math.sin((arcAngle - 90 - adjustedGap) * Math.PI / 180);
const [startX, startY, endX, endY] = flip ? [coordX2, coordY2, coordX1, coordY1] : [coordX1, coordY1, coordX2, coordY2];
const dPath = `M ${startX},${startY} A ${realRadius},${realRadius} 0 ${largeArcFlag} ${sweepFlag} ${endX},${endY}`;
return { dPath };
}
calcularExpresionCSS(cssExpression) {
return cssNumber(this, cssExpression, 0, "angle");
}
};
var resizing = /* @__PURE__ */ new WeakMap();
var Orbit = {
refresh: refreshLayout,
resize(parentElementSelector) {
var _a, _b;
const parent = typeof parentElementSelector === "string" ? (_a = globalThis.document) == null ? void 0 : _a.querySelector(parentElementSelector) : parentElementSelector;
if (!(parent == null ? void 0 : parent.ownerDocument)) {
console.error("Orbit.resize: element not found:", parentElementSelector);
return () => {
};
}
(_b = resizing.get(parent)) == null ? void 0 : _b();
const view = parent.ownerDocument.defaultView;
const applyRatio = (width) => {
if (!(width > 0)) return;
for (const element of parent.querySelectorAll(".gravity-spot")) {
const ratio = String(width / 500);
if (element.style.getPropertyValue("--o-force-ratio") !== ratio) element.style.setProperty("--o-force-ratio", ratio);
}
refreshLayout(parent);
};
const observer = new view.ResizeObserver((entries) => {
for (const entry of entries) applyRatio(entry.contentRect.width);
});
observer.observe(parent);
applyRatio(parent.clientWidth || parent.getBoundingClientRect().width);
const stop = () => {
observer.disconnect();
if (resizing.get(parent) === stop) resizing.delete(parent);
};
resizing.set(parent, stop);
return stop;
}
};
function registerOrbit() {
if (typeof customElements === "undefined" || typeof document === "undefined") return;
if (!customElements.get("o-progress")) customElements.define("o-progress", OrbitProgress);
if (!customElements.get("o-arc")) customElements.define("o-arc", OrbitArc);
requestLayout(document);
window.Orbit = Orbit;
}
registerOrbit();
// node_modules/zumly/dist/zumly.mjs
var it = Symbol.for("zumly.viewLifecycles");
function ee(r) {
try {
let t = r();
t && typeof t.then == "function" && t.catch((e) => console.error("Zumly: view cleanup failed:", e));
} catch (t) {
console.error("Zumly: view cleanup failed:", t);
}
}
function se() {
let r = false, t = [], e = { onCleanup(s) {
if (typeof s != "function") throw new TypeError("Zumly: onCleanup expects a function");
r ? ee(s) : t.push(s);
}, attach(s) {
let i = s[it];
i || (i = /* @__PURE__ */ new Set(), Object.defineProperty(s, it, { value: i, configurable: true })), i.add(e);
}, dispose() {
if (!r) {
r = true;
for (let s of t.splice(0).reverse()) ee(s);
}
} };
return e;
}
function C(r) {
if (!r) return;
let t = [];
r[it] && t.push(r);
let s = (r.ownerDocument || r).createTreeWalker(r, 1), i;
for (; i = s.nextNode(); ) i[it] && t.push(i);
for (let n = t.length - 1; n >= 0; n--) {
let o = t[n], a = o[it];
if (a) {
for (let c of a) c.dispose();
delete o[it];
}
}
}
var Ve = 1e4;
var xt = class {
#t = {};
constructor(t = {}) {
this.#t = t;
}
#e(t) {
return typeof t == "function" ? "function" : t instanceof HTMLElement ? "element" : typeof t == "object" && t !== null && typeof t.render == "function" ? "object" : typeof t != "string" ? "unknown" : /^https?:\/\/|^\/|\.(?:html|php)(?:[?#].*)?$/i.test(t) ? "url" : t.includes("<") ? "html" : t.includes("-") ? "webcomponent" : "unknown";
}
async resolve(t, e = null) {
let s = /* @__PURE__ */ new Set();
for (; typeof t == "string" && Object.prototype.hasOwnProperty.call(this.#t, t); ) {
if (s.has(t)) throw new Error(`Zumly: circular view alias "${t}"`);
s.add(t), t = this.#t[t];
}
let i = this.#e(t);
switch (i) {
case "html": {
let n = document.createElement("div");
n.innerHTML = t.trim();
let o = n.firstElementChild;
if (!o) throw new Error("Zumly: view produced no element (html)");
return o;
}
case "url": {
let n = new AbortController(), o = setTimeout(() => n.abort(), Ve), a;
try {
let f = await fetch(t, { signal: n.signal });
if (!f.ok) throw new Error(`Zumly: fetch failed for "${t}" (${f.status})`);
a = await f.text();
} finally {
clearTimeout(o);
}
let c = document.createElement("div");
c.innerHTML = a.trim();
let l = c.firstElementChild;
if (!l) throw new Error("Zumly: view produced no element (url)");
return l;
}
case "function":
case "object": {
let n = se(), o = e?.target || document.createElement("div");
n.attach(o);
let a = { ...e, target: o, props: e?.props || {}, context: e?.context || /* @__PURE__ */ new Map(), onCleanup: n.onCleanup };
try {
let c = await (i === "function" ? t(a) : t.render(a)), l;
if (typeof c == "string") l = await this.resolve(c, a);
else if (c instanceof HTMLElement) l = c;
else if (i === "function") l = o, o.classList.length || (o.style.width || (o.style.width = "100%"), o.style.height || (o.style.height = "100%"));
else throw new Error("Zumly: view render() must return a string or HTMLElement");
return n.attach(l), l;
} catch (c) {
throw n.dispose(), c;
}
}
case "element":
return t.cloneNode(true);
case "webcomponent":
return await customElements.whenDefined(t), document.createElement(t);
default:
throw new Error("Zumly: unknown view type for source");
}
}
};
async function ut(r, t, e, s, i, n) {
if (!r || !r.classList) {
let o = document.createElement("div");
o.classList.add("z-view"), r && o.appendChild(r), r = o;
} else r.classList.contains("z-view") || r.classList.add("z-view");
r.dataset.viewName = t, r.getAttribute("aria-label") || r.setAttribute("aria-label", `View: ${t}`), r.style.transformOrigin = "0 0", r.style.position = "absolute", s ? r.classList.add("is-current-view") : r.classList.add("is-new-current-view", "has-no-events", "hide"), e.append(r);
try {
i[t] && typeof i[t] == "object" && typeof i[t].mounted == "function" && await i[t].mounted();
} catch (o) {
throw C(r), r.remove(), o;
}
return r;
}
function $(r, t, e) {
t && e === "welcome" && console.info(`%c Zumly %c ${t}`, "background: #424085; color: white; border-radius: 3px;", "color: #424085"), t && r && (e === "info" || e === void 0) && console.info(`%c Zumly %c ${t}`, "background: #6679A3; color: #304157; border-radius: 3px;", "color: #6679A3"), t && e === "warn" && console.warn(`%c Zumly %c ${t}`, "background: #DCBF53; color: #424085; border-radius: 3px;", "color: #424085"), t && e === "error" && console.error(`%c Zumly %c ${t}`, "background: #BE4747; color: white; border-radius: 3px;", "color: #424085");
}
function re(r, t) {
if (t.isValid = false, !r || typeof r != "object") {
$(false, "'options' object has to be provided when instance is defined", "error");
return;
}
if (typeof r.mount != "string") {
$(false, "'mount' must be a string selector", "error");
return;
}
if (t.mount = r.mount, typeof r.initialView != "string") {
$(false, "'initialView' must be a string", "error");
return;
}
if (t.initialView = r.initialView, !r.views || typeof r.views != "object") {
$(false, "'views' must be an object", "error");
return;
}
t.views = r.views, t.isValid = true, t.preload = Array.isArray(r.preload) ? r.preload : [], t.debug = typeof r.debug == "boolean" ? r.debug : false, t.componentContext = r.componentContext && typeof r.componentContext == "object" ? r.componentContext : /* @__PURE__ */ new Map();
let e = r.transitions && typeof r.transitions == "object" ? r.transitions : null, s = e && e.cover, i = typeof s == "string" ? s.toLowerCase() : null, n = i === "width" || i === "height";
t.cover = n ? i : "width", !n && e && s !== void 0 && $(false, `'transitions.cover' must be either "width" or "height". Falling back to "width".`, "warn"), t.duration = e && typeof e.duration == "string" ? e.duration : "1s", t.ease = e && typeof e.ease == "string" ? e.ease : "ease-in-out", t.deferred = typeof r.deferred == "boolean" ? r.deferred : false;
let o = e && e.hideTrigger;
o === true || o === "fade" ? t.hideTrigger = o : (t.hideTrigger = false, o != null && o !== false && $(false, `'transitions.hideTrigger' must be true or "fade". Falling back to false.`, "warn"));
let a = e && e.effects;
Array.isArray(a) && a.length >= 2 && typeof a[0] == "string" && typeof a[1] == "string" ? t.effects = [a[0], a[1]] : Array.isArray(a) && a.length === 1 && typeof a[0] == "string" ? t.effects = [a[0], a[0]] : (t.effects = ["none", "none"], a != null && $(false, `'transitions.effects' must be an array of 1-2 CSS filter strings (e.g. ["blur(3px)", "blur(8px) saturate(0)"]). Falling back to no effects.`, "warn"));
let c = e && e.driver, l = typeof c == "string" ? c.toLowerCase() : null, f = typeof c == "function" ? c : null;
if (l !== null) {
let p = ["css", "waapi", "none", "anime", "gsap", "motion"];
t.transitionDriver = p.includes(l) ? l : "css", p.includes(l) || $(false, `'transitions.driver' must be "css", "waapi", "none", "anime", "gsap", "motion", or a function. Got "${c}". Falling back to "css".`, "warn");
} else f !== null ? t.transitionDriver = f : t.transitionDriver = "css";
let h = e && e.stagger;
typeof h == "number" && h > 0 ? t.stagger = h : (t.stagger = 0, h != null && h !== 0 && $(false, "'transitions.stagger' must be a positive number (ms). Falling back to 0 (disabled).", "warn")), t.parallax = 0;
let u = r.lateralNav;
if (u === false) t.lateralNav = false;
else if (u && typeof u == "object") {
let p = typeof u.mode == "string" ? u.mode.toLowerCase() : null, y = p === "auto" || p === "always";
p && !y && $(false, `'lateralNav.mode' must be "auto" or "always". Falling back to "auto".`, "warn");
let g = null;
if (Array.isArray(u.siblings)) g = u.siblings.filter((x) => typeof x == "string" && x), g.length < 2 && ($(false, "'lateralNav.siblings' needs at least 2 view names. Ignoring.", "warn"), g = null);
else if (u.siblings && typeof u.siblings == "object") {
g = {};
for (let [x, b] of Object.entries(u.siblings)) if (Array.isArray(b)) {
let _ = b.filter((S) => typeof S == "string" && S);
_.length >= 2 && (g[x] = _);
}
Object.keys(g).length === 0 && (g = null);
} else u.siblings !== void 0 && $(false, "'lateralNav.siblings' must be an array of view names or a { parentView: [...] } map. Ignoring.", "warn");
t.lateralNav = { mode: y ? p : "auto", arrows: typeof u.arrows == "boolean" ? u.arrows : true, dots: typeof u.dots == "boolean" ? u.dots : true, keepAlive: u.keepAlive === true || u.keepAlive === "visible" ? u.keepAlive : false, siblings: g };
} else t.lateralNav = { mode: "auto", arrows: true, dots: true, keepAlive: false, siblings: null };
let d = r.depthNav;
if (d === false) t.depthNav = false;
else if (d && typeof d == "object") {
let p = typeof d.position == "string" ? d.position.toLowerCase() : null, y = p === "bottom-left" || p === "top-left";
p && !y && $(false, `'depthNav.position' must be "bottom-left" or "top-left". Falling back to "bottom-left".`, "warn"), t.depthNav = { position: y ? p : "bottom-left" };
} else t.depthNav = { position: "bottom-left" };
let v = r.lateralNav && typeof r.lateralNav == "object" ? r.lateralNav.position : void 0;
if (t.lateralNav && typeof t.lateralNav == "object") {
let p = v === "bottom-center" || v === "top-center";
v && !p && $(false, `'lateralNav.position' must be "bottom-center" or "top-center". Falling back to "bottom-center".`, "warn"), t.lateralNav.position = p ? v : "bottom-center";
}
let m = r.inputs;
m === false ? t.inputs = { wheel: false, keyboard: false, click: false, touch: false } : m && typeof m == "object" ? t.inputs = { wheel: typeof m.wheel == "boolean" ? m.wheel : true, keyboard: typeof m.keyboard == "boolean" ? m.keyboard : true, click: typeof m.click == "boolean" ? m.click : true, touch: typeof m.touch == "boolean" ? m.touch : true } : t.inputs = { wheel: true, keyboard: true, click: true, touch: true };
}
function St(r, t, e, s, i) {
let n = e / r, o = s / t;
return i === "height" ? { scale: o, scaleInv: 1 / o } : { scale: n, scaleInv: 1 / n };
}
function Dt(r, t, e, s) {
let i = t.left, n = t.top, o = r.x - i + (r.width - e.width * s) / 2, a = r.y - n + (r.height - e.height * s) / 2;
return `translate(${o}px, ${a}px) scale(${s})`;
}
function ie(r, t, e) {
let s = t.left, i = t.top, n = r.x - s + (r.width - e.width) / 2, o = r.y - i + (r.height - e.height) / 2;
return `translate(${n}px, ${o}px)`;
}
function ne(r, t) {
let e = r.x + r.width / 2 - t.x, s = r.y + r.height / 2 - t.y;
return `${e}px ${s}px`;
}
function oe(r, t, e, s, i = 0) {
let n = r.width / 2 - t.width / 2 - t.x + e.x, o = r.height / 2 - t.height / 2 - t.y + e.y, a = 1 - i, c = n * a, l = o * a;
return { x: n, y: o, transform: `translate(${c}px, ${l}px) scale(${s})` };
}
function ae({ canvasRect: r, canvasOffset: t, triggerRect: e, previousViewRectAtBaseTransform: s, lastViewZoomedElementRect: i, previousViewRectWithPreviousAtEndTransform: n, scale: o, preScale: a, parallax: c = 0 }) {
let l = t.left, f = t.top, h = r.width / 2 - e.width / 2 - e.x, u = r.height / 2 - e.height / 2 - e.y, d = h + (s.x - i.x) + n.x - l + (n.width - i.width) / 2, v = u + (s.y - i.y) + n.y - f + (n.height - i.height) / 2, m = 1 - Math.min(c * 2, 0.9);
return `translate(${d * m}px, ${v * m}px) scale(${o * a})`;
}
function le(r, t, e, s, i) {
return `translate(${r - e.left}px, ${t - e.top}px) scale(${s * i})`;
}
function nt(r) {
let t = r.split(/\s+/);
return { x: parseFloat(t[0]) || 0, y: parseFloat(t[1]) || 0 };
}
function et(r) {
let t = typeof r == "string" ? r : "", e = t.match(/translate\s*\(\s*([-+\d.eE]+)px\s*,\s*([-+\d.eE]+)px\s*\)/);
if (!e) return { tx: 0, ty: 0, rest: t.trim(), matched: false };
let s = t.replace(/translate\s*\([^)]+\)\s*/, "").trim();
return { tx: parseFloat(e[1]), ty: parseFloat(e[2]), rest: s, matched: true };
}
function Y(r) {
let t = et(r), e = r.match(/scale\s*\(\s*([-+\d.eE]+)\s*\)/);
return { tx: t.matched ? t.tx : 0, ty: t.matched ? t.ty : 0, scale: e ? parseFloat(e[1]) : 1 };
}
function dt(r, t, e, s, i, n, o, a, c, l) {
let f = t.x - e.x * (1 - n) - s, h = t.y - e.y * (1 - n) - i, u = t.width / n, d = t.height / n, v = f + e.x + (r.x - f - e.x - s) / n, m = h + e.y + (r.y - h - e.y - i) / n, p = r.width / n, y = r.height / n, g = f + o.x + a + l * (v - f - o.x), x = h + o.y + c + l * (m - h - o.y), b = p * l, _ = y * l;
return { x: g, y: x, width: b, height: _, left: g, top: x, right: g + b, bottom: x + _ };
}
function mt(r, t, e) {
return { viewName: r, backwardState: t, forwardState: e };
}
function ce(r) {
return { detachedNode: r };
}
function fe(r, t, e, s, i) {
let n = [];
return t !== null && n.push(t), e !== null && n.push(e), s !== null && n.push(s), i !== null && n.push(i), { zoomLevel: r, views: n };
}
function Ft(r) {
let t = r.views?.[3];
if (!(!t || !(t.detachedNode instanceof Node))) return t.detachedNode;
}
var Tt = class {
#t = /* @__PURE__ */ new Map();
#e;
constructor({ maxEntries: t = 64 } = {}) {
this.#e = Number.isInteger(t) && t >= 0 ? t : 64;
}
set(t, e, s = null) {
this.#e !== 0 && this.adopt(t, e.cloneNode(true), s);
}
adopt(t, e, s = null) {
let i = Date.now();
for (let [n, o] of this.#t) o.expires !== null && i >= o.expires && this.#t.delete(n);
if (this.#e !== 0) for (this.#t.delete(t), this.#t.set(t, { node: e, expires: s === null ? null : i + s }); this.#t.size > this.#e; ) this.#t.delete(this.#t.keys().next().value);
}
get(t) {
let e = this.#t.get(t);
return e ? e.expires !== null && Date.now() >= e.expires ? (this.#t.delete(t), null) : (this.#t.delete(t), this.#t.set(t, e), e.node.cloneNode(true)) : null;
}
has(t) {
let e = this.#t.get(t);
return e ? e.expires !== null && Date.now() >= e.expires ? (this.#t.delete(t), false) : true : false;
}
invalidate(t) {
this.#t.delete(t);
}
clear() {
this.#t.clear();
}
};
var je = "button, input, select, textarea, a[href], summary";
function kt(r) {
return r?.matches?.(je) || false;
}
function Vt(r) {
return !!r?.closest?.('input, textarea, select, [contenteditable]:not([contenteditable="false"]), [role="textbox"], [role="combobox"], [role="slider"], [role="spinbutton"]');
}
function he(r) {
let t = r.querySelectorAll(".zoom-me[data-to]");
return t.forEach((e) => {
kt(e) || (e.hasAttribute("role") || e.setAttribute("role", "button"), e.hasAttribute("tabindex") || e.setAttribute("tabindex", "0")), !e.hasAttribute("aria-label") && !e.hasAttribute("aria-labelledby") && !e.textContent.trim() && e.setAttribute("aria-label", `Zoom to ${e.dataset.to}`);
}), t;
}
var Et = class {
constructor() {
this.backgrounds = /* @__PURE__ */ new Map();
}
sync(t) {
for (let e of t.children) e.classList.contains("z-view") && (e.classList.contains("is-current-view") ? this.restore(e) : (this.backgrounds.has(e) || this.backgrounds.set(e, { inert: e.inert, hidden: e.getAttribute("aria-hidden") }), e.inert = true, e.setAttribute("aria-hidden", "true")));
for (let e of this.backgrounds.keys()) t.contains(e) || this.restore(e);
}
restore(t) {
let e = this.backgrounds.get(t);
e && (t.inert = e.inert, e.hidden === null ? t.removeAttribute("aria-hidden") : t.setAttribute("aria-hidden", e.hidden), this.backgrounds.delete(t));
}
destroy() {
for (let t of this.backgrounds.keys()) this.restore(t);
}
};
var Be = 300 * 1e3;
var ue = /^https?:\/\/|^\/|\.(?:html|php)(?:[?#].*)?$/i;
var zt = class {
#t;
#e;
#l;
#n = /* @__PURE__ */ new Map();
#s = /* @__PURE__ */ new Map();
#r = null;
#c = 0;
#d;
#f;
#i = false;
#o = false;
constructor(t = {}, { maxCacheEntries: e = 64, prefetchConcurrency: s = 2, maxPendingPrefetch: i = 32 } = {}) {
this.#l = t, this.#t = new xt(t), this.#e = new Tt({ maxEntries: e }), this.#d = Number.isInteger(s) && s > 0 ? s : 2, this.#f = Number.isInteger(i) && i >= 0 ? i : 32;
}
#h(t) {
let e = /* @__PURE__ */ new Set();
for (; typeof t == "string" && Object.prototype.hasOwnProperty.call(this.#l, t); ) {
if (e.has(t)) return null;
e.add(t), t = this.#l[t];
}
return typeof t == "string" && (t.includes("<") || ue.test(t)) ? t : null;
}
#v(t) {
return ue.test(t) ? Be : null;
}
async get(t, e = null) {
if (this.#i) throw new Error("Zumly: view prefetcher has been destroyed");
this.#s.delete(t);
let s = this.#h(t);
if (s !== null) {
let i = this.#e.get(t);
return i || (await this.#m(t, s, e)).cloneNode(true);
}
return this.#t.resolve(t, e);
}
#m(t, e, s) {
if (this.#n.has(t)) return this.#n.get(t);
let i = (async () => {
try {
let n = await this.#t.resolve(t, s);
return this.#i || this.#e.adopt(t, n, this.#v(e)), n;
} finally {
this.#n.delete(t);
}
})();
return this.#n.set(t, i), i;
}
async #u(t, e) {
if (this.#i) return;
this.#s.delete(t);
let s = this.#h(t);
s === null || this.#e.has(t) || await this.#m(t, s, e);
}
async preloadEager(t, e = null) {
!Array.isArray(t) || t.length === 0 || await Promise.all([...new Set(t)].map((s) => this.#u(s, e)));
}
prefetch(t, e = null) {
if (this.#o) {
this.#p(t, e);
return;
}
this.#u(t, e).catch(() => {
});
}
prefetchOnHover(t, e = null) {
this.prefetch(t, e);
}
scanAndPrefetch(t, e = null) {
if (this.#i || !t || !t.querySelectorAll) return;
let s = he(t);
this.#s.clear();
let i = /* @__PURE__ */ new Set();
for (let n of s) {
let o = n.dataset.to;
if (!(!o || i.has(o))) {
if (i.add(o), this.#s.size >= this.#f) break;
this.#p(o, e);
}
}
this.#a();
}
#p(t, e) {
this.#i || this.#s.size >= this.#f || this.#s.has(t) || this.#h(t) === null || this.#e.has(t) || this.#n.has(t) || this.#s.set(t, e);
}
#a() {
this.#i || this.#o || this.#r !== null || this.#s.size === 0 || this.#c >= this.#d || (this.#r = setTimeout(() => {
if (this.#r = null, this.#i || this.#s.size === 0) return;
let [t, e] = this.#s.entries().next().value;
this.#s.delete(t), this.#c++, this.#u(t, e).catch(() => {
}).finally(() => {
this.#c--, this.#a();
}), this.#a();
}, 0));
}
pause() {
this.#o = true, this.#r !== null && clearTimeout(this.#r), this.#r = null;
}
resume() {
this.#o = false, this.#a();
}
destroy() {
this.#i = true, this.#r !== null && clearTimeout(this.#r), this.#r = null, this.#s.clear(), this.#e.clear();
}
};
var Lt = Symbol.for("zumly.viewVisibility");
function de(r) {
r[Lt] || Object.defineProperty(r, Lt, { configurable: true, value: { value: r.style.getPropertyValue("content-visibility"), priority: r.style.getPropertyPriority("content-visibility") } });
}
function Ct(r) {
r && (de(r), r.style.contentVisibility = "hidden");
}
function pt(r) {
r && (de(r), r.style.contentVisibility = "visible");
}
function vt(r) {
let t = r?.[Lt];
t && (t.value ? r.style.setProperty("content-visibility", t.value, t.priority) : r.style.removeProperty("content-visibility"), delete r[Lt]);
}
function K(r) {
if (typeof r == "number" && !Number.isNaN(r)) return Math.max(0, r);
let t = String(r).trim(), e = t.match(/^(\d*\.?\d+)\s*(ms|s)?$/i);
if (!e) return typeof console < "u" && console.warn(`[zumly] Could not parse duration "${t}" \u2014 falling back to 500ms.`), 500;
let s = parseFloat(e[1]);
return (e[2] || "s").toLowerCase() === "ms" ? Math.max(0, s) : Math.max(0, s * 1e3);
}
function Nt(r) {
return K(r) / 1e3;
}
function k(r, t) {
if (r.classList.contains("is-new-current-view")) {
let e = t.views[0].forwardState;
r.classList.replace("is-new-current-view", "is-current-view"), r.classList.remove("zoom-current-view", "has-no-events"), r.style.transformOrigin = e.origin, r.style.transform = e.transform;
return;
}
if (r.classList.contains("is-previous-view")) {
let e = t.views[1].forwardState;
r.classList.remove("zoom-previous-view", "has-no-events"), r.style.transformOrigin = e.origin, r.style.transform = e.transform;
return;
}
if (r.classList.contains("is-last-view")) {
let e = t.views[2].forwardState;
r.classList.remove("zoom-last-view", "has-no-events"), r.style.transformOrigin = e.origin, r.style.transform = e.transform;
}
}
function V(r, t) {
r.classList.remove("zoom-previous-view-reverse", "has-no-events", "has-effect", "has-effect-reverse"), r.style.removeProperty("--z-effect-filter"), r.style.transformOrigin = "0 0", r.style.transform = t.transform;
}
function j(r, t) {
r.classList.remove("zoom-last-view-reverse", "has-no-events", "has-effect", "has-effect-reverse"), r.style.removeProperty("--z-effect-filter"), r.style.transformOrigin = t.origin, r.style.transform = t.transform;
}
function H(r, t) {
C(r);
try {
t && t.removeChild(r);
} catch {
try {
r?.parentElement && t && (C(r.parentElement), t.removeChild(r.parentElement));
} catch {
}
}
}
function Z(...r) {
for (let t of r) t && (t.classList.remove("hide"), pt(t));
}
function G(r, t) {
let { currentView: e, previousView: s, backView: i, backViewState: n, lastView: o, lastViewState: a, incomingTransformEnd: c, currentStage: l, canvas: f } = r, h = l.views[0];
Z(e), e.classList.replace("is-new-current-view", "is-current-view"), e.classList.remove("zoom-current-view", "has-no-events"), e.style.transformOrigin = h.forwardState.origin, e.style.transform = c || h.forwardState.transform, i && n && (i.style.transform = n.transformEnd), o && a && (o.style.transform = a.transformEnd), r.keepAlive || H(s, f), t();
}
var Q = 150;
function J(r, t) {
let e = false, s = setTimeout(() => {
e || (e = true, r());
}, t);
return { finish() {
e || (e = true, clearTimeout(s), r());
}, extend(i) {
e || (clearTimeout(s), s = setTimeout(() => {
e || (e = true, r());
}, i));
}, safetyTimer: s };
}
function Ot() {
return { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 };
}
function Re(r) {
if (!r || r === "none") return Ot();
let t = String(r).match(/matrix\(([-\d.eE]+),\s*([-\d.eE]+),\s*([-\d.eE]+),\s*([-\d.eE]+),\s*([-\d.eE]+),\s*([-\d.eE]+)\)/);
return t ? { a: parseFloat(t[1]), b: parseFloat(t[2]) || 0, c: parseFloat(t[3]) || 0, d: parseFloat(t[4]), e: parseFloat(t[5]) || 0, f: parseFloat(t[6]) || 0 } : Ot();
}
function at(r) {
return `matrix(${r.a}, ${r.b}, ${r.c}, ${r.d}, ${r.e}, ${r.f})`;
}
function ot(r, t, e) {
return r + (t - r) * e;
}
function lt(r, t, e) {
let s = Math.max(0, Math.min(1, e));
return { a: ot(r.a, t.a, s), b: ot(r.b, t.b, s), c: ot(r.c, t.c, s), d: ot(r.d, t.d, s), e: ot(r.e, t.e, s), f: ot(r.f, t.f, s) };
}
function me(r, t, e) {
r.style.transformOrigin = t, r.style.transform = e;
try {
r.getBoundingClientRect();
} catch {
}
return Re(getComputedStyle(r).transform);
}
function ve(r, t) {
let { type: e, currentView: s, previousView: i, lastView: n, currentStage: o, duration: a, ease: c, canvas: l } = r;
if (!s || !i || !o) {
t();
return;
}
e === "lateral" ? qe(r, t) : e === "zoomIn" ? We(s, i, n, o, a, c, t) : e === "zoomOut" ? Xe(s, i, n, o, a, c, l, t) : t();
}
function qe(r, t) {
let { currentView: e, previousView: s, backView: i, backViewState: n, lastView: o, lastViewState: a, incomingTransformStart: c, incomingTransformEnd: l, outgoingTransform: f, outgoingTransformEnd: h, currentStage: u, duration: d, ease: v, canvas: m } = r, p = K(d), y = u.views[0];
Z(e), e.classList.replace("is-new-current-view", "is-current-view"), e.classList.remove("zoom-current-view", "has-no-events"), e.style.transformOrigin = y.forwardState.origin, i && n && (R(i, d, v, { "--lateral-from": n.transformStart, "--lateral-to": n.transformEnd }), i.classList.add("zoom-lateral-back")), o && a && (R(o, d, v, { "--lateral-from": a.transformStart, "--lateral-to": a.transformEnd }), o.classList.add("zoom-lateral-back")), r.keepAlive !== "visible" && (R(s, d, v, { "--lateral-out-from": f, "--lateral-out-to": h }), s.classList.add("zoom-lateral-out")), R(e, d, v, { "--lateral-in-from": c, "--lateral-in-to": l }), e.classList.add("zoom-lateral-in");
let g = r.keepAlive === "visible" ? [e] : [s, e];
i && n && g.push(i), o && a && g.push(o);
let x = g.length, b = p + Q, _ = /* @__PURE__ */ new Set(["zoom-lateral-in", "zoom-lateral-out", "zoom-lateral-back"]), { finish: S, extend: O } = J(() => {
jt(g, E, A), r.keepAlive ? (s.classList.remove("zoom-lateral-out"), s.style.opacity = "") : H(s, m), i && (i.classList.remove("zoom-lateral-back"), i.style.transform = n?.transformEnd || i.style.transform), o && (o.classList.remove("zoom-lateral-back"), o.style.transform = a?.transformEnd || o.style.transform), e.classList.remove("zoom-lateral-in"), e.style.transform = l, t();
}, b);
function A(T) {
T.target !== T.currentTarget || !_.has(T.animationName) || O(b);
}
function E(T) {
if (T.target !== T.currentTarget || !_.has(T.animationName)) return;
let P = T.currentTarget;
P.removeEventListener("animationend", E), P.removeEventListener("animationstart", A), x--, x <= 0 && S();
}
g.forEach((T) => {
T.addEventListener("animationstart", A), T.addEventListener("animationend", E);
});
}
function We(r, t, e, s, i, n, o) {
Z(r, t, e);
let a = s.stagger || 0;
R(r, i, n, { "--current-view-transform-start": s.views[0].backwardState.transform, "--current-view-transform-end": s.views[0].forwardState.transform }), a > 0 && r.style.setProperty("animation-delay", "0ms"), R(t, i, n, { "--previous-view-transform-start": s.views[1].backwardState.transform, "--previous-view-transform-end": s.views[1].forwardState.transform }), a > 0 && t.style.setProperty("animation-delay", `${a}ms`), e && (R(e, i, n, { "--last-view-transform-start": s.views[2].backwardState.transform, "--last-view-transform-end": s.views[2].forwardState.transform }), a > 0 && e.style.setProperty("animation-delay", `${a * 2}ms`)), r.classList.add("zoom-current-view"), t.classList.add("zoom-previous-view"), e && e.classList.add("zoom-last-view");
let c = e ? [r, t, e] : [r, t], l = c.length, f = K(i), h = e ? a * 2 : a, u = f + h + Q, d = /* @__PURE__ */ new Set(["zoom-current-view", "zoom-previous-view", "zoom-last-view"]), { finish: v, extend: m } = J(() => {
jt(c, y, p), c.forEach((g) => g.style.removeProperty("animation-delay")), c.forEach((g) => {
if (g?.isConnected) try {
k(g, s);
} catch {
}
}), o();
}, u);
function p(g) {
g.target !== g.currentTarget || !d.has(g.animationName) || m(u);
}
function y(g) {
if (g.target !== g.currentTarget || !d.has(g.animationName)) return;
let x = g.currentTarget;
if (x.removeEventListener("animationend", y), x.removeEventListener("animationstart", p), x.isConnected) try {
k(x, s);
} catch {
}
l--, l <= 0 && v();
}
c.forEach((g) => {
g.addEventListener("animationstart", p), g.addEventListener("animationend", y);
});
}
function Xe(r, t, e, s, i, n, o, a) {
let c = s.views[0], l = s.views[1], f = e && s.views[2] ? s.views[2] : null, h = s.stagger || 0;
R(r, i, n, { "--current-view-transform-start": c.backwardState.transform, "--current-view-transform-end": c.forwardState.transform }), h > 0 && r.style.setProperty("animation-delay", "0ms"), R(t, i, n, { "--previous-view-transform-start": l.backwardState.transform, "--previous-view-transform-end": l.forwardState.transform }), h > 0 && t.style.setProperty("animation-delay", `${h}ms`), e && f && (R(e, i, n, { "--last-view-transform-start": f.backwardState.transform, "--last-view-transform-end": f.forwardState.transform }), h > 0 && e.style.setProperty("animation-delay", `${h * 2}ms`)), r.classList.add("zoom-current-view-reverse"), t.classList.add("zoom-previous-view-reverse"), e && e.classList.add("zoom-last-view-reverse");
let u = e ? [r, t, e] : [r, t], d = u.length, v = K(i), m = e ? h * 2 : h, p = v + m + Q, y = /* @__PURE__ */ new Set(["zoom-current-view-reverse", "zoom-previous-view-reverse", "zoom-last-view-reverse"]), { finish: g, extend: x } = J(() => {
jt(u, _, b), u.forEach((S) => S.style.removeProperty("animation-delay")), u.forEach((S) => {
if (S?.isConnected) try {
pe(S, s, o);
} catch {
}
}), a();
}, p);
function b(S) {
S.target !== S.currentTarget || !y.has(S.animationName) || x(p);
}
function _(S) {
if (S.target !== S.currentTarget || !y.has(S.animationName)) return;
let O = S.currentTarget;
if (O.removeEventListener("animationend", _), O.removeEventListener("animationstart", b), O.isConnected) try {
pe(O, s, o);
} catch {
}
d--, d <= 0 && g();
}
u.forEach((S) => {
S.addEventListener("animationstart", b), S.addEventListener("animationend", _);
});
}
function pe(r, t, e) {
if (r.classList.contains("zoom-current-view-reverse")) {
H(r, e);
return;
}
if (r.classList.contains("zoom-previous-view-reverse")) {
V(r, t.views[1].backwardState);
return;
}
r.classList.contains("zoom-last-view-reverse") && j(r, t.views[2].backwardState);
}
function R(r, t, e, s) {
r.style.setProperty("--zoom-duration", t), r.style.setProperty("--zoom-ease", e);
for (let [i, n] of Object.entries(s)) r.style.setProperty(i, n);
}
function jt(r, t, e) {
for (let s of r) s?.removeEventListener && (s.removeEventListener("animationend", t), s.removeEventListener("animationstart", e));
}
function ye(r, t) {
let { type: e, currentView: s, previousView: i, lastView: n, currentStage: o, canvas: a } = r;
if (!s || !i || !o) {
t();
return;
}
if (e === "lateral") {
G(r, t);
return;
}
if (e === "zoomIn") {
Z(s, i, n), k(s, o), k(i, o), n && k(n, o), t();
return;
}
if (e === "zoomOut") {
H(s, a), Z(i, n), V(i, o.views[1].backwardState), n && j(n, o.views[2].backwardState), t();
return;
}
t();
}
function ge(r, t) {
let { type: e, currentView: s, previousView: i, lastView: n, currentStage: o, duration: a, ease: c, canvas: l } = r;
if (!s || !i || !o) {
t();
return;
}
let f = K(a);
if (e === "lateral") {
Ue(r, f, c, t);
return;
}
if (e === "zoomIn") {
Ye(s, i, n, o, f, c, t);
return;
}
if (e === "zoomOut") {
Ke(s, i, n, o, f, c, l, t);
return;
}
t();
}
function Ye(r, t, e, s, i, n, o) {
Z(r, t, e);
let a = s.views[0], c = s.views[1], l = e && s.views[2] ? s.views[2] : null;
r.style.transformOrigin = a.backwardState.origin, r.style.transform = a.backwardState.transform, t.style.transformOrigin = c.backwardState.origin, t.style.transform = c.backwardState.transform, l && (e.style.transformOrigin = l.backwardState.origin, e.style.transform = l.backwardState.transform);
let f = s.stagger || 0, h = [];
h.push(r.animate([{ transform: a.backwardState.transform }, { transform: a.forwardState.transform }], { duration: i, easing: n, fill: "forwards" })), h.push(t.animate([{ transform: c.backwardState.transform }, { transform: c.forwardState.transform }], { duration: i, delay: f, easing: n, fill: "forwards" })), l && h.push(e.animate([{ transform: l.backwardState.transform }, { transform: l.forwardState.transform }], { duration: i, delay: f * 2, easing: n, fill: "forwards" }));
let u = l ? f * 2 : f, { finish: d } = J(() => {
Bt(h);
try {
k(r, s), k(t, s), e && k(e, s);
} catch {
}
o();
}, i + u + Q);
Promise.all(h.map((v) => v.finished)).then(d).catch(d);
}
function Ke(r, t, e, s, i, n, o, a) {
let c = s.views[0], l = s.views[1], f = e && s.views[2] ? s.views[2] : null, h = l.backwardState, u = f ? f.backwardState : null;
r.style.transformOrigin = c.forwardState.origin, r.style.transform = c.forwardState.transform, requestAnimationFrame(() => {
requestAnimationFrame(() => {
t.style.transformOrigin = l.forwardState.origin, e && f && (e.style.transformOrigin = f.forwardState.origin);
let d = t.style.transform || getComputedStyle(t).transform || l.forwardState.transform, v = e && f ? e.style.transform || getComputedStyle(e).transform || f.forwardState.transform : null, m = s.stagger || 0, p = [];
p.push(r.animate([{ transform: c.forwardState.transform }, { transform: c.backwardState.transform }], { duration: i, easing: n, fill: "forwards" })), p.push(t.animate([{ transform: d }, { transform: h.transform }], { duration: i, delay: m, easing: n, fill: "both" })), e && u && p.push(e.animate([{ transform: v }, { transform: u.transform }], { duration: i, delay: m * 2, easing: n, fill: "both" })), t.style.removeProperty("transform"), e && e.style.removeProperty("transform");
let y = e && u ? m * 2 : m, { finish: g } = J(() => {
Bt(p);
try {
H(r, o), V(t, h), e && u && j(e, u);
} catch {
}
a();
}, i + y + Q);
Promise.all(p.map((x) => x.finished)).then(g).catch(g);
});
});
}
function Ue(r, t, e, s) {
let { currentView: i, previousView: n, backView: o, backViewState: a, lastView: c, lastViewState: l, incomingTransformStart: f, incomingTransformEnd: h, outgoingTransform: u, outgoingTransformEnd: d, currentStage: v, canvas: m } = r, p = v.views[0];
Z(i), i.classList.replace("is-new-current-view", "is-current-view"), i.classList.remove("zoom-current-view", "has-no-events"), i.style.transformOrigin = p.forwardState.origin;
let y = [];
y.push(i.animate([{ transform: f, opacity: 0 }, { transform: h, opacity: 1 }], { duration: t, easing: e, fill: "forwards" })), r.keepAlive !== "visible" && y.push(n.animate([{ transform: u, opacity: 1 }, { transform: d, opacity: 0 }], { duration: t, easing: e, fill: "forwards" })), o && a && y.push(o.animate([{ transform: a.transformStart }, { transform: a.transformEnd }], { duration: t, easing: e, fill: "forwards" })), c && l && y.push(c.animate([{ transform: l.transformStart }, { transform: l.transformEnd }], { duration: t, easing: e, fill: "forwards" }));
let { finish: g } = J(() => {
Bt(y), i.style.transform = h || p.forwardState.transform, o && a && (o.style.transform = a.transformEnd), c && l && (c.style.transform = l.transformEnd), r.keepAlive ? (n.style.opacity = "", n.style.transform = d) : H(n, m), s();
}, t + Q);
Promise.all(y.map((x) => x.finished)).then(g).catch(g);
}
function Bt(r) {
for (let t of r) try {
t.cancel();
} catch {
}
}
var be = "[-+]?(?:\\d*\\.\\d+|\\d+)(?:[eE][-+]?\\d+)?";
var we = `(${be})(px)?`;
var Ge = new RegExp(`^(?:translate\\(\\s*${we}\\s*,\\s*${we}\\s*\\)\\s*)?(?:scale\\(\\s*(${be})\\s*\\))?$`);
function Qe(r) {
if (typeof r != "string") return null;
let t = r.trim();
if (t === "none") return Ot();
if (!t) return null;
let e = t.match(Ge);
if (!e) return null;
let s = Number(e[1] ?? 0), i = Number(e[3] ?? 0), n = Number(e[5] ?? 1);
return s !== 0 && !e[2] || i !== 0 && !e[4] || ![s, i, n].every(Number.isFinite) ? null : { a: n, b: 0, c: 0, d: n, e: s, f: i };
}
function q(r, t, e) {
let s = Qe(e);
return s ? (r.style.transformOrigin = t, r.style.transform = e, s) : me(r, t, e);
}
function _e(r, t) {
let e = typeof globalThis < "u" && globalThis.anime;
if (!e || typeof e != "function") {
console.warn('Zumly anime driver: Anime.js not loaded. Add <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.2/anime.min.js"><\/script>'), t();
return;
}
let { type: s, currentView: i, previousView: n, lastView: o, currentStage: a, duration: c, ease: l, canvas: f } = r;
if (!i || !n || !a) {
t();
return;
}
let h = K(c);
s === "lateral" ? G(r, t) : s === "zoomIn" ? Je(e, i, n, o, a, h, l, t) : s === "zoomOut" ? ts(e, i, n, o, a, h, l, f, t) : t();
}
function Je(r, t, e, s, i, n, o, a) {
Z(t, e, s);
let c = i.views[0], l = i.views[1], f = s && i.views[2] ? i.views[2] : null, h = xe([{ el: t, backward: c.backwardState, forward: c.forwardState }, { el: e, backward: l.backwardState, forward: l.forwardState }, ...f ? [{ el: s, backward: f.backwardState, forward: f.forwardState }] : []], "forward");
Se(h, 0);
let u = i.stagger || 0, d = n + (f ? u * 2 : u), v = { value: 0 };
r({ targets: v, value: 1, duration: d, easing: Ee(o), update: () => {
let m = v.value * d;
Te(h, m, n, u);
}, complete: () => {
k(t, i), k(e, i), s && k(s, i), a();
} });
}
function ts(r, t, e, s, i, n, o, a, c) {
let l = i.views[0], f = i.views[1], h = s && i.views[2] ? i.views[2] : null, u = f.backwardState, d = h ? h.backwardState : null, v = xe([{ el: t, backward: l.backwardState, forward: l.forwardState }, { el: e, backward: f.backwardState, forward: f.forwardState }, ...h ? [{ el: s, backward: h.backwardState, forward: h.forwardState }] : []], "backward");
Se(v, 0);
let m = i.stagger || 0, p = n + (h ? m * 2 : m), y = { value: 0 };
r({ targets: y, value: 1, duration: p, easing: Ee(o), update: () => {
let g = y.value * p;
Te(v, g, n, m);
}, complete: () => {
H(t, a), V(e, u), s && d && j(s, d), c();
} });
}
function xe(r, t) {
return r.map(({ el: e, backward: s, forward: i }) => {
if (t === "forward") {
let n = q(e, s.origin, s.transform), o = q(e, s.origin, i.transform);
return { el: e, from: n, to: o };
} else {
let n = q(e, i.origin, i.transform), o = q(e, i.origin, s.transform);
return { el: e, from: n, to: o };
}
});
}
function Se(r, t) {
for (let { el: e, from: s, to: i } of r) e.style.transform = at(lt(s, i, t));
}
function Te(r, t, e, s) {
for (let i = 0; i < r.length; i++) {
let { el: n, from: o, to: a } = r[i], c = i * s, l = Math.max(0, t - c), f = e > 0 ? Math.min(1, l / e) : 1;
n.style.transform = at(lt(o, a, f));
}
}
function Ee(r) {
if (typeof r != "string") return "easeInOutQuad";
let t = r.toLowerCase();
return t === "linear" ? "linear" : t.includes("ease-in-out") ? "easeInOutQuad" : t.includes("ease-in") ? "easeInQuad" : t.includes("ease-out") ? "easeOutQuad" : t;
}
function ke(r, t) {
let e = typeof globalThis < "u" && globalThis.gsap;
if (!e || typeof e.to != "function") {
console.warn('Zumly GSAP driver: GSAP not loaded. Add <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"><\/script>'), t();
return;
}
let { type: s, currentView: i, previousView: n, lastView: o, currentStage: a, duration: c, ease: l, canvas: f } = r;
if (!i || !n || !a) {
t();
return;
}
let h = Nt(c);
s === "lateral" ? G(r, t) : s === "zoomIn" ? es(e, i, n, o, a, h, l, t) : s === "zoomOut" ? ss(e, i, n, o, a, h, l, f, t) : t();
}
function es(r, t, e, s, i, n, o, a) {
Z(t, e, s);
let c = i.views[0], l = i.views[1], f = s && i.views[2] ? i.views[2] : null;
At(t, c.backwardState), At(e, l.backwardState), f && At(s, f.backwardState);
let h = r.timeline({ onComplete: () => {
k(t, i), k(e, i), s && k(s, i), a();
} }), u = ze(o), d = (i.stagger || 0) / 1e3;
h.to(t, { transform: c.forwardState.transform, duration: n, ease: u }, 0), h.to(e, { transform: l.forwardState.transform, duration: n, ease: u }, d), f && h.to(s, { transform: f.forwardState.transform, duration: n, ease: u }, d * 2);
}
function ss(r, t, e, s, i, n, o, a, c) {
let l = i.views[0], f = i.views[1], h = s && i.views[2] ? i.views[2] : null, u = f.backwardState, d = h ? h.backwardState : null;
At(t, l.forwardState);
let v = e.style.transform || getComputedStyle(e).transform || f.forwardState.transform, m = s && h ? s.style.transform || getComputedStyle(s).transform || h.forwardState.transform : null;
e.style.transformOrigin = f.forwardState.origin, s && h && (s.style.transformOrigin = h.forwardState.origin);
let p = ze(o), y = r.timeline({ onComplete: () => {
H(t, a), V(e, u), s && d && j(s, d), c();
} }), g = (i.stagger || 0) / 1e3;
r.set(e, { transform: v }), s && m && r.set(s, { transform: m }), y.fromTo(t, { transform: l.forwardState.transform }, { transform: l.backwardState.transform, duration: n, ease: p }, 0), y.fromTo(e, { transform: v }, { transform: u.transform, duration: n, ease: p }, g), s && d && y.fromTo(s, { transform: m }, { transform: d.transform, duration: n, ease: p }, g * 2);
}
function At(r, t) {
r.style.transformOrigin = t.origin, r.style.transform = t.transform;
}
function ze(r) {
if (typeof r != "string") return "power2.inOut";
let t = r.toLowerCase();
return t === "linear" ? "none" : t.includes("ease-in-out") ? "power2.inOut" : t.includes("ease-in") ? "power2.in" : t.includes("ease-out") ? "power2.out" : t;
}
function Le(r, t) {
let e = ns();
if (!e || typeof e != "function") {
console.warn('Zumly Motion driver: Motion not loaded. Add <script src="https://cdn.jsdelivr.net/npm/motion@11/dist/motion.min.js"><\/script>'), t();
return;
}
let { type: s, currentView: i, previousView: n, lastView: o, currentStage: a, duration: c, ease: l, canvas: f } = r;
if (!i || !n || !a) {
t();
return;
}
let h = Nt(c);
s === "lateral" ? G(r, t) : s === "zoomIn" ? rs(e, i, n, o, a, h, l, t) : s === "zoomOut" ? is(e, i, n, o, a, h, l, f, t) : t();
}
function rs(r, t, e, s, i, n, o, a) {
Z(t, e, s);
let c = Ce(t, e, s, i, "forward");
Oe(c, 0);
let l = i.stagger || 0, f = l / 1e3, h = n * 1e3, u = n + (c.length > 2 ? f * 2 : f);
r(0, 1, { duration: u, ease: Ae(o), onUpdate: (v) => {
let m = v * u * 1e3;
Ne(c, m, h, l);
} }).then(() => {
k(t, i), k(e, i), s && k(s, i), a();
}).catch(() => a());
}
function is(r, t, e, s, i, n, o, a, c) {
let l = i.views[1], f = s && i.views[2] ? i.views[2] : null, h = l.backwardState, u = f ? f.backwardState : null, d = Ce(t, e, s, i, "backward");
Oe(d, 0);
let v = i.stagger || 0, m = v / 1e3, p = n * 1e3, y = n + (d.length > 2 ? m * 2 : m);
r(0, 1, { duration: y, ease: Ae(o), onUpdate: (x) => {
let b = x * y * 1e3;
Ne(d, b, p, v);
} }).then(() => {
H(t, a), V(e, h), s && u && j(s, u), c();
}).catch(() => c());
}
function Ce(r, t, e, s, i) {
let n = s.views[0], o = s.views[1], a = e && s.views[2] ? s.views[2] : null, c = [{ el: r, backward: n.backwardState, forward: n.forwardState }, { el: t, backward: o.backwardState, forward: o.forwardState }];
return a && c.push({ el: e, backward: a.backwardState, forward: a.forwardState }), c.map(({ el: l, backward: f, forward: h }) => i === "forward" ? { el: l, from: q(l, f.origin, f.transform), to: q(l, f.origin, h.transform) } : { el: l, from: q(l, h.origin, h.transform), to: q(l, h.origin, f.transform) });
}
function Oe(r, t) {
for (let { el: e, from: s, to: i } of r) e.style.transform = at(lt(s, i, t));
}
function Ne(r, t, e, s) {
for (let i = 0; i < r.length; i++) {
let { el: n, from: o, to: a } = r[i], c = i * s, l = Math.max(0, t - c), f = e > 0 ? Math.min(1, l / e) : 1;
n.style.transform = at(lt(o, a, f));
}
}
function ns() {
let r = typeof globalThis < "u" ? globalThis : typeof window < "u" ? window : {};
return r.motion?.animate || r.Motion?.animate || r.animate;
}
function Ae(r) {
if (typeof r != "string") return "easeInOut";
let t = r.toLowerCase();
return t === "linear" ? "linear" : t.includes("ease-in-out") ? "easeInOut" : t.includes("ease-in") ? "easeIn" : t.includes("ease-out") ? "easeOut" : r;
}
var Pe = { css: ve, waapi: ge, none: ye, anime: _e, gsap: ke, motion: Le };
function Pt(r) {
if (typeof r == "function") return { runTransition(s, i) {
r(s, i);
} };
let t = typeof r == "string" ? r.toLowerCase() : "css";
return { runTransition: Pe[t] || Pe.css };
}
function os(r) {
if (typeof r != "string" || r.trim() === "") return { tx: 0, ty: 0, rest: "" };
let t = et(r);
return t.matched ? { tx: t.tx, ty: t.ty, rest: t.rest } : null;
}
function Ie(r, t, e) {
let s = os(r);
if (s === null) return r;
let i = s.tx * t, n = s.ty * e, o = s.rest ? ` ${s.rest}` : "";
return `translate(${i}px, ${n}px)${o}`.trim();
}
function as(r) {
if (typeof r != "string" || !r.trim()) return null;
let t = r.trim().match(/^([-\d.eE]+)px\s+([-\d.eE]+)px$/);
return t ? { x: parseFloat(t[1]), y: parseFloat(t[2]) } : null;
}
function $e(r, t, e) {
let s = as(r);
return s === null ? r : `${s.x * t}px ${s.y * e}px`;
}
function Rt(r, t, e, s, i) {
if (!r.storedViews?.length || !r.canvas || t <= 0 || e <= 0) return;
let n = s / t, o = i / e, a = /* @__PURE__ */ new Set(), c = (p) => {
if (!(!p || p.detachedNode)) for (let y of [p.backwardState, p.forwardState]) !y || a.has(y) || (a.add(y), y.transform = Ie(y.transform, n, o), y.origin != null && (y.origin = $e(y.origin, n, o)));
}, l = [...r.storedViews];
for (let p of r.lateralHistory || []) p.stage && l.push(p.stage), c(p.entry);
for (let p of l) Array.isArray(p.views) && p.views.forEach(c);
let f = r.canvas, h = f.querySelector(".is-current-view"), u = f.querySelector(".is-previous-view"), d = f.querySelector(".is-last-view"), v = f.querySelector(".is-new-current-view"), m = [h, u, d, v].filter(Boolean);
for (let p of m) {
if (!p.style) continue;
let y = p.style.transform;
y != null && y !== "" && (p.style.transform = Ie(y, n, o));
let g = p.style.transformOrigin;
g != null && g !== "" && (p.style.transformOrigin = $e(g, n, o));
}
}
var ls = 8e3;
var gt = class {
constructor(t) {
if (this.storedViews = [], this.currentStage = null, this.debug = false, this.trace = [], this.blockEvents = false, this._blockEventsSafetyTimer = null, this.touchstartX = 0, this.touchstartY = 0, this.touchendX = 0, this.touchendY = 0, this.touching = false, this.lateralHistory = [], this._lastCanvasWidth = 0, this._lastCanvasHeight = 0, this._pendingResizeCorrection = false, this._destroyed = false, this._trackedTimers = /* @__PURE__ */ new Set(), this._pendingTransitionComplete = null, this._navigationTask = null, this._initPromise = null, this._accessibility = new Et(), this._hooks = {}, this._plugins = [], re(t, this), !this.isValid) {
this.notify("is unable to start: invalid or missing required options (mount, initialView, views).", "error");
return;
}
if (this.canvas = document.querySelector(this.mount), !this.canvas) {
this.notify(`mount selector "${this.mount}" did not match any element.`, "error"), this.isValid = false;
return;
}
this.transitionDriver = Pt(this.transitionDriver), this._onZoom = this.onZoom.bind(this), this._onTouchStart = this.onTouchStart.bind(this), this._onTouchEnd = this.onTouchEnd.bind(this), this._onTouchCancel = () => {
this.touching = false;
}, this._onKeyUp = this.onKeyUp.bind(this), this._onKeyDown = this.onKeyDown.bind(this), this._onKeyboardClick = (e) => {
e.target.closest?.(".zoom-me[data-to]") && ((e.detail === 0 ? !this.inputs.keyboard : !this.inputs.click) || (e.preventDefault(), e.detail === 0 && this.onZoom(e)));
}, this._onWheel = this.onWheel.bind(this), this._wheelCooldown = false, this._onPrefetchTrigger = (e) => {
let s = e.target.closest?.(".zoom-me[data-to]");
!this._destroyed && s && this.canvas.contains(s) && this.prefetcher.prefetch(s.dataset.to, { trigger: s, context: this.componentContext, props: { ...s.dataset } });
}, this._onResize = this._handleResize.bind(this), this._resizeDebounceTimer = null, this._RESIZE_DEBOUNCE_MS = 80, this.prefetcher = new zt(this.views), this._bindEvents();
}
_bindEvents() {
let t = this.canvas;
t && (this._canvasAttributes = new Map(["tabindex", "role", "aria-roledescription", "aria-live"].map((e) => [e, t.getAttribute(e)])), t.setAttribute("tabindex", "0"), t.setAttribute("role", "application"), t.setAttribute("aria-roledescription", "zoomable interface"), t.setAttribute("aria-live", "polite"), t.addEventListener("mouseup", this._onZoom, false), t.addEventListener("touchend", this._onZoom, false), t.addEventListener("touchstart", this._onTouchStart, { passive: true }), t.addEventListener("touchend", this._onTouchEnd, false), t.addEventListener("touchcancel", this._onTouchCancel, { passive: true }), t.addEventListener("keyup", this._onKeyUp, false), t.addEventListener("keydown", this._onKeyDown, false), t.addEventListener("click", this._onKeyboardClick, false), t.addEventListener("wheel", this._onWheel, { passive: false }), t.addEventListener("mouseover", this._onPrefetchTrigger, { passive: true }), t.addEventListener("focusin", this._onPrefetchTrigger, { passive: true }), window.addEventListener("resize", this._onResize, { passive: true }), typeof ResizeObserver < "u" && (this._resizeObserver = new ResizeObserver(() => this._handleResize()), this._resizeObserver.observe(t)));
}
_unbindEvents() {
let t = this.canvas;
t && (t.removeEventListener("mouseup", this._onZoom, false), t.removeEventListener("touchend", this._onZoom, false), t.removeEventListener("touchstart", this._onTouchStart), t.removeEventListener("touchend", this._onTouchEnd, false), t.removeEventListener("touchcancel", this._onTouchCancel), t.removeEventListener("keyup", this._onKeyUp, false), t.removeEventListener("keydown", this._onKeyDown, false), t.removeEventListener("click", this._onKeyboardClick, false), t.removeEventListener("wheel", this._onWheel), t.removeEventListener("mouseover", this._onPrefetchTrigger), t.removeEventListener("focusin", this._onPrefetchTrigger)), window.removeEventListener("resize", this._onResize), this._resizeObserver && (this._resizeObserver.disconnect(), this._resizeObserver = null);
}
_resetCanvasScroll(t) {
if (t && ((t.scrollLeft !== 0 || t.scrollTop !== 0) && (this.notify(`canvas had a residual scroll (${t.scrollLeft}, ${t.scrollTop}) \u2014 resetting. Something scrolled the canvas mid-transition.`, "warn"), t.scrollLeft = 0, t.scrollTop = 0), !this._warnedScrolledAncestor)) {
for (let e = t.parentElement; e && e !== document.body; e = e.parentElement) if (e.scrollLeft !== 0 || e.scrollTop !== 0) {
let s = e.className ? `.${String(e.className).trim().split(/\s+/).join(".")}` : e.tagName.toLowerCase();
this.notify(`an ancestor of the canvas (${s}) is scrolled (${e.scrollLeft}, ${e.scrollTop}): all zooms will land displaced. Use 'overflow: clip' (not 'hidden') on containers around the canvas.`, "warn"), this._warnedScrolledAncestor = true;
break;
}
}
}
_setBlockEvents() {
this.blockEvents = true, this._clearBlockEventsSafety(), this._blockEventsSafetyTimer = setTimeout(() => {
if (this.blockEvents && !this._destroyed) {
this.notify("blockEvents safety timeout: driver did not call onComplete. Force-resetting.", "warn"), this.blockEvents = false;
let t = this._pendingTransitionComplete;
t ? t() : this._onTransitionComplete();
}
}, ls);
}
_clearBlockEventsSafety() {
this._blockEventsSafetyTimer !== null && (clearTimeout(this._blockEventsSafetyTimer), this._blockEventsSafetyTimer = null);
}
_setTrackedTimeout(t, e) {
let s = setTimeout(() => {
this._trackedTimers.delete(s), !this._destroyed && t();
}, e);
return this._trackedTimers.add(s), s;
}
_clearTrackedTimeouts() {
for (let t of this._trackedTimers) clearTimeout(t);
this._trackedTimers.clear();
}
storeViews(t) {
this.tracing("storedViews()"), this.storedViews.push(t), this.debug && console.debug("Zumly storedViews", t);
}
tracing(t) {
if (this.debug) if (t === "ended") {
let e = this.trace.map((s, i) => `${i === 0 ? `Instance ${this.mount}: ${s}` : `${s}`}`).join(" > ");
this.notify(e), this.trace = [];
} else this.trace.push(t);
}
notify(t, e) {
return $(this.debug, t, e);
}
_recordCanvasSize() {
this.canvas && (this._lastCanvasWidth = this.canvas.offsetWidth, this._lastCanvasHeight = this.canvas.offsetHeight);
}
_handleResize() {
this._destroyed || (this._resizeDebounceTimer && clearTimeout(this._resizeDebounceTimer), this._resizeDebounceTimer = setTimeout(() => {
if (this._resizeDebounceTimer = null, !this.isValid || !this.canvas || this._lastCanvasWidth === 0) return;
let t = this.canvas.offsetWidth, e = this.canvas.offsetHeight;
if (!(t === this._lastCanvasWidth && e === this._lastCanvasHeight)) {
if (this.blockEvents) {
this._pendingResizeCorrection = true;
return;
}
Rt(this, this._lastCanvasWidth, this._lastCanvasHeight, t, e), this._lastCanvasWidth = t, this._lastCanvasHeight = e;
}
}, this._RESIZE_DEBOUNCE_MS));
}
_onTransitionComplete() {
if (this._clearBlockEventsSafety(), this._pendingResizeCorrection && !this.blockEvents) {
if (this._pendingResizeCorrection = false, this.canvas && this._lastCanvasWidth > 0) {
let e = this.canvas.offsetWidth, s = this.canvas.offsetHeight;
(e !== this._lastCanvasWidth || s !== this._lastCanvasHeight) && Rt(this, this._lastCanvasWidth, this._lastCanvasHeight, e, s), this._lastCanvasWidth = e, this._lastCanvasHeight = s;
}
} else this._recordCanvasSize();
for (let e of this.canvas?.children || []) e.classList.contains("z-view") && vt(e);
let t = this.canvas?.querySelector(".is-current-view");
t && this._accessibility.restore(t), this._manageFocus(), this.canvas && this._accessibility.sync(this.canvas);
}
_manageFocus() {
if (!this.canvas) return;
let t = this.canvas.querySelector(".is-current-view");
if (!t) return;
let e = 'a[href], button:not([disabled]), input:not([disabled]):not([type="hidden"]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])', s = (n) => !n || n.closest("[inert], [hidden]") ? false : (n.focus({ preventScroll: true }), document.activeElement === n || n.contains(document.activeElement)), i = t.querySelector(e);
if (!s(i)) {
for (let n of t.querySelectorAll(e)) if (n !== i && s(n)) return;
t.setAttribute("tabindex", "-1"), t.focus({ preventScroll: true });
}
}
_prefersReducedMotion() {
return typeof window.matchMedia == "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
}
_resolveEffects(t) {
if (this._prefersReducedMotion()) return ["none", "none"];
let e = t?.dataset?.withEffects;
if (e && typeof e == "string") {
let s = e.split("|").map((i) => i.trim());
return [s[0] || "none", s[1] || s[0] || "none"];
}
return this.effects;
}
_applyEffects(t, e, s, i, n) {
!s || s[0] === "none" && s[1] === "none" || (t && s[0] !== "none" && (t.style.setProperty("--z-effect-filter", s[0]), t.style.setProperty("--zoom-duration", i), t.style.setProperty("--zoom-ease", n), t.classList.add("has-effect")), e && s[1] !== "none" && (e.style.setProperty("--z-effect-filter", s[1]), e.style.setProperty("--zoom-duration", i), e.style.setProperty("--zoom-ease", n), e.classList.add("has-effect")));
}
_removeEffect(t) {
if (!t || !t.classList.contains("has-effect")) return;
t.classList.remove("has-effect"), t.classList.add("has-effect-reverse");
let e = () => {
t.classList.remove("has-effect-reverse"), t.style.removeProperty("--z-effect-filter"), t.removeEventListener("transitionend", e);
};
t.addEventListener("transitionend", e, { once: true });
let s = t.style.getPropertyValue("--zoom-duration");
this._setTrackedTimeout(e, s ? parseFloat(s) * (s.includes("ms") ? 1 : 1e3) + 100 : 1100);
}
_resolveHideTrigger(t) {
return t?.dataset?.hideTrigger !== void 0 ? t.dataset.hideTrigger === "fade" ? "fade" : true : this.hideTrigger;
}
_applyHideTrigger(t, e, s, i, n, o = true) {
if (!(!s || !t)) if (s === "fade") {
if (t.style.setProperty("--zoom-duration", i), t.style.setProperty("--zoom-ease", n), t.classList.add("z-trigger-fade"), !o) return;
e.style.setProperty("--zoom-duration", i), e.style.setProperty("--zoom-ease", n), e.classList.add("z-view-fade-in"), e.offsetHeight, e.classList.remove("hide");
} else t.classList.add("z-trigger-hidden");
}
_restoreHideTrigger(t, e, s, i) {
if (!(!e || !t)) if (e === "fade") {
t.classList.remove("z-trigger-fade"), t.style.setProperty("--zoom-duration", s), t.style.setProperty("--zoom-ease", i), t.classList.add("z-trigger-fade-reverse");
let n = () => {
t.classList.remove("z-trigger-fade-reverse"), t.style.removeProperty("--zoom-duration"), t.style.removeProperty("--zoom-ease"), t.removeEventListener("transitionend", n);
};
t.addEventListener("transitionend", n, { once: true }), this._setTrackedTimeout(n, parseFloat(s) * (s.includes("ms") ? 1 : 1e3) + 100);
} else t.classList.remove("z-trigger-hidden");
}
on(t, e) {
return typeof e != "function" ? this : ((this._hooks[t] ||= []).push(e), this);
}
use(t, e) {
if (!t) return this;
let s = { plugin: t, options: e };
return this._plugins.push(s), this._initialized && this._installPlugin(s), this;
}
_installPlugin(t) {
let { plugin: e, options: s } = t;
try {
typeof e == "function" ? e(this, s) : typeof e.install == "function" && e.install(this, s);
} catch (i) {
this.notify(`plugin install error: ${i.message}`, "error");
}
}
off(t, e) {
return e ? this._hooks[t] && (this._hooks[t] = this._hooks[t].filter((s) => s !== e)) : delete this._hooks[t], this;
}
_emit(t, e = {}) {
let s = this._hooks[t];
if (!(!s || s.length === 0)) for (let i of s) try {
i(e);
} catch (n) {
this.debug && console.error(`Zumly hook "${t}" threw:`, n);
}
}
_runNavigation(t) {
if (this._destroyed || this.blockEvents) return Promise.resolve();
let e = { pendingViews: /* @__PURE__ */ new Set() };
e.promise = new Promise((i) => {
e.resolve = i;
}), this._navigationTask = e, this.blockEvents = true, this.prefetcher?.pause();
let s = () => {
this._navigationTask === e && (this._navigationTask = null, this.blockEvents = false, this._clearBlockEventsSafety(), this.prefetcher?.resume()), e.resolve();
};
try {
Promise.resolve(t()).then(s, (i) => {
this.notify(`navigation aborted: ${i.message}`, "error"), s();
});
} catch (i) {
this.notify(`navigation aborted: ${i.message}`, "error"), s();
}
return e.promise;
}
_cancelNavigation() {
let t = this._navigationTask;
if (this._navigationTask = null, this._pendingTransitionComplete = null, this._clearBlockEventsSafety(), this.blockEvents = false, t) {
for (let e of t.pendingViews) C(e);
t.pendingViews.clear(), t.releaseTransition?.(), t.resolve();
}
}
_createViewContext(t = {}, e) {
let s = { target: document.createElement("div"), context: this.componentContext, props: t };
return e && (s.trigger = e), this._navigationTask?.pendingViews.add(s.target), s;
}
_runNavigationTransition(t, e) {
if (this._destroyed) return Promise.resolve();
let s = this._navigationTask;
return new Promise((i) => {
s && (s.releaseTransition = i);
let n = () => {
if (this._pendingTransitionComplete === n) {
if (this._pendingTransitionComplete = null, this._destroyed) {
i();
return;
}
try {
Promise.resolve(e()).then(i, (l) => {
this.notify(`transition completion failed: ${l.message}`, "error"), i();
});
} catch (l) {
this.notify(`transition completion failed: ${l.message}`, "error"), i();
}
}
};
this._setBlockEvents(), this._pendingTransitionComplete = n;
let o = this._prefersReducedMotion?.(), a = o ? Pt("none") : this.transitionDriver, c = o ? { ...t, duration: "0s", currentStage: { ...t.currentStage, stagger: 0 } } : t;
try {
a.runTransition(c, n);
} catch (l) {
this.notify(`transition driver failed: ${l.message}. Finishing without animation.`, "error");
try {
Pt("none").runTransition(c, n);
} catch {
n();
}
}
});
}
zoomLevel() {
return this.storedViews.length;
}
getZoomLevel() {
return this.zoomLevel();
}
getCurrentViewName() {
return !this.storedViews || this.storedViews.length === 0 ? null : this.storedViews[this.storedViews.length - 1]?.views?.[0]?.viewName ?? null;
}
async back() {
if (!this._destroyed) {
if (this.blockEvents) {
this.notify("back ignored: a transition is already running.", "warn");
return;
}
if (this.lateralHistory && this.lateralHistory.length > 0) {
let t = this.lateralHistory.pop(), e = t && typeof t == "object" ? t.name : t, s = t && typeof t == "object" ? t.entry : void 0, i = t && typeof t == "object" ? t.node : void 0;
return this._doLateral(e, true, { savedEntry: s, keepAliveNode: i, savedStage: t?.stage });
}
return this.zoomOut();
}
}
async goTo(t, e = {}) {
return this._destroyed ? void 0 : (e.mode === "lateral" ? "lateral" : "depth") === "depth" ? this.zoomTo(t, e) : this._doLateral(t, false, e);
}
async zoomTo(t, e = {}) {
if (this._destroyed) return;
if (!this.isValid || !this.canvas) {
this.notify("zoomTo() cannot run: instance is invalid or canvas not found.", "error");
return;
}
if (typeof t != "string" || !t) {
this.notify("zoomTo() requires a non-empty view name.", "warn");
return;
}
if (!Object.prototype.hasOwnProperty.call(this.views, t)) {
this.notify(`zoomTo("${t}"): view not found in views. Available: ${Object.keys(this.views).join(", ")}`, "warn");
return;
}
if (this.getCurrentViewName() === t) return;
let i = this.canvas.getBoundingClientRect(), n = Math.max(40, i.width * 0.1), o = Math.max(40, i.height * 0.1), c = { rect: { x: i.left + (i.width - n) / 2, y: i.top + (i.height - o) / 2, width: n, height: o }, duration: e.duration ?? this.duration, ease: e.ease ?? this.ease, props: e.props ?? {} };
await this._doZoomIn(t, c);
}
init() {
return this._destroyed || this._initialized ? Promise.resolve() : this._initPromise ? this._initPromise : !this.isValid || !this.canvas ? (this.notify("init() cannot run: instance is invalid or canvas element was not found.", "error"), Promise.resolve()) : (this._initPromise = this._runNavigation(() => this._initialize()).finally(() => {
this._initPromise = null;
}), this._initPromise);
}
async _initialize() {
this.tracing("init()");
let t = this.prefetcher, e = this._createViewContext();
if (this.preload && this.preload.length && (await t.preloadEager(this.preload, null).catch((i) => {
this.notify(`preload failed: ${i.message}`, "warn");
}), this._destroyed)) return;
let s;
try {
let i = await t.get(this.initialView, e);
if (this._destroyed) {
C(i);
return;
}
if (s = await ut(i, this.initialView, this.canvas, true, this.views, this.componentContext), this._destroyed) {
C(s), s.remove();
return;
}
} catch (i) {
this.notify(`init() failed to resolve initial view "${this.initialView}": ${i.message}`, "error");
return;
}
if (this._emit("viewMounted", { viewName: this.initialView, node: s }), !this._destroyed) {
t.scanAndPrefetch(s, e), this.storeViews({ zoomLevel: this.storedViews.length, scale: 1, views: [{ viewName: this.initialView, backwardState: { origin: "0 0", transform: "" } }] }), this.currentStage = this.storedViews[this.storedViews.length - 1], this._recordCanvasSize(), this._accessibility.sync(this.canvas), this._initialized = true;
for (let i of this._plugins) this._installPlugin(i);
}
}
destroy() {
if (!this._destroyed) {
this._destroyed = true, this._emit("destroy"), this._cancelNavigation(), this._clearBlockEventsSafety(), this._clearTrackedTimeouts(), this._pendingTransitionComplete = null, this._resizeDebounceTimer && (clearTimeout(this._resizeDebounceTimer), this._resizeDebounceTimer = null), this._unbindEvents(), this._removeNav(), this._cleanupLateralKeepAlive(), this._accessibility.destroy(), this.canvas && C(this.canvas);
for (let t of this.storedViews) {
let e = Ft(t);
e && C(e);
}
if (this.blockEvents = false, this.storedViews = [], this.currentStage = null, this.lateralHistory = [], this.trace = [], this._hooks = {}, this.prefetcher?.destroy(), this.prefetcher = null, this.transitionDriver = null, this.canvas) for (let [t, e] of this._canvasAttributes || []) e === null ? this.canvas.removeAttribute(t) : this.canvas.setAttribute(t, e);
this.isValid = false;
}
}
async _doZoomIn(t, e) {
return this._runNavigation(() => this._performZoomIn(t, e));
}
async _performZoomIn(t, e) {
if (this._emit("beforeZoomIn", { viewName: t }), this._destroyed) return;
this.tracing("zoomIn()");
let s = this.canvas, i = e.el;
this._resetCanvasScroll(s);
let o = (this.storedViews.length > 0 ? this.storedViews[this.storedViews.length - 1] : null)?.scale ?? 1;
this.tracing("getView()");
let a = this._createViewContext(i ? { ...i.dataset } : e.props ?? {}, i), c = i?.dataset?.deferred !== void 0 ? true : this.deferred, l, f = null, h = () => {
C(f), C(l), l?.remove();
};
try {
if (c) {
let w = await this.prefetcher.get(t, a);
if (this._destroyed) {
C(w);
return;
}
for (f = document.createDocumentFragment(); w.firstChild; ) f.appendChild(w.firstChild);
l = await ut(w, t, s, false, {}, this.componentContext);
} else {
let w = await this.prefetcher.get(t, a);
if (this._destroyed) {
C(w);
return;
}
this.prefetcher.scanAndPrefetch(w, a), l = await ut(w, t, s, false, this.views, this.componentContext);
}
} catch (w) {
h(), this.notify(`zoomIn aborted: failed to resolve view "${t}": ${w.message}`, "error");
return;
}
if (!l) {
h();
return;
}
if (this._destroyed) {
h();
return;
}
if (c || this._emit("viewMounted", { viewName: t, node: l }), this._destroyed) return;
i && i.classList.add("zoomed");
let u = s.getBoundingClientRect(), d = u.left, v = u.top, m = i ? (() => {
let w = i.getBoundingClientRect();
return { x: w.x, y: w.y, width: w.width, height: w.height };
})() : e.rect, p = i ? i.dataset.withDuration || this.duration : e.duration ?? this.duration, y = i ? i.dataset.withEase || this.ease : e.ease ?? this.ease, g = i ? i.dataset.withCover || this.cover : e.cover ?? this.cover, x = i ? parseInt(i.dataset.withStagger, 10) || this.stagger : e.stagger ?? this.stagger, b = s.querySelector(".is-current-view"), _ = s.querySelector(".is-previous-view"), S = s.querySelector(".is-last-view");
if (!b) {
this.notify("zoomIn aborted: no current view found in canvas. Call init() before navigating and keep Zumly view classes intact.", "error"), i && i.classList.remove("zoomed"), h();
return;
}
this._cleanupLateralKeepAlive(), this.lateralHistory = [], Ct(l), S && (Ct(S), s.removeChild(S));
let O, A, E, T, P, B, z, D = b.style.transform, N = b.style.transformOrigin, I = _ ? _.style.transform : null, F = _ ? _.style.transformOrigin : null;
try {
b.classList.replace("is-current-view", "is-previous-view"), _ && _.classList.replace("is-previous-view", "is-last-view");
let w = l.getBoundingClientRect(), L = b.getBoundingClientRect(), X = null, Wt = null, bt = null;
_ && (X = _.getBoundingClientRect(), bt = _.querySelector(".zoomed"), Wt = bt && bt.getBoundingClientRect ? bt.getBoundingClientRect() : X);
let _t = { left: d, top: v }, Xt = { width: u.width, height: u.height }, Yt = { width: w.width, height: w.height }, Kt = St(m.width, m.height, w.width, w.height, g);
z = Kt.scale;
let Me = Kt.scaleInv;
O = Dt(m, _t, Yt, Me), E = b.style.transform;
let Ut = ne(m, L);
T = oe(Xt, m, L, z, this.parallax);
let Gt = nt(N || "0 0"), st = Y(E || ""), Qt = nt(Ut), rt = Y(T.transform), Jt = m;
if (i && (Jt = dt(m, L, Gt, st.tx, st.ty, st.scale, Qt, rt.tx, rt.ty, rt.scale)), A = ie(Jt, _t, Yt), _) {
P = _.style.transform;
let He = dt(L, L, Gt, st.tx, st.ty, st.scale, Qt, rt.tx, rt.ty, rt.scale), De = le(T.x, T.y, _t, z, o), te = nt(F || "0 0"), Mt = Y(P || ""), Ht = Y(De), Fe = dt(Wt, X, te, Mt.tx, Mt.ty, Mt.scale, te, Ht.tx, Ht.ty, Ht.scale);
B = ae({ canvasRect: Xt, canvasOffset: _t, triggerRect: m, previousViewRectAtBaseTransform: L, lastViewZoomedElementRect: Fe, previousViewRectWithPreviousAtEndTransform: He, scale: z, preScale: o, parallax: this.parallax });
}
l.style.transform = O, b.style.transformOrigin = Ut;
} catch (w) {
this.notify(`zoomIn geometry computation failed: ${w.message}. Aborting zoom.`, "error"), this.debug && console.error("Zumly _doZoomIn error:", w), b.style.transform = D, b.style.transformOrigin = N, b.classList.contains("is-previous-view") && b.classList.replace("is-previous-view", "is-current-view"), _ && (_.style.transform = I, _.style.transformOrigin = F, _.classList.contains("is-last-view") && _.classList.replace("is-last-view", "is-previous-view")), h(), S && s.prepend(S), i && i.classList.remove("zoomed"), vt(b), vt(_), vt(S);
return;
}
let M = mt(l.dataset.viewName, { origin: l.style.transformOrigin, duration: p, ease: y, transform: O }, { origin: l.style.transformOrigin, duration: p, ease: y, transform: A }), wt = mt(b.dataset.viewName, { origin: b.style.transformOrigin, duration: p, ease: y, transform: E }, { origin: b.style.transformOrigin, duration: p, ease: y, transform: T.transform }), ft = _ ? mt(_.dataset.viewName, { origin: _.style.transformOrigin, duration: p, ease: y, transform: P }, { origin: _.style.transformOrigin, duration: p, ease: y, transform: B }) : null, U = S ? ce(S) : null, W = fe(this.storedViews.length, M, wt, ft, U);
W.scale = z, W.stagger = x || 0, this.storeViews(W), this.currentStage = this.storedViews[this.storedViews.length - 1], this.tracing("setCSSVariables()");
let It = this._resolveEffects(i);
this._applyEffects(b, _, It, p, y);
let ht = this._resolveHideTrigger(i);
this._applyHideTrigger(i, l, ht, p, y), ht && (W.hideTriggerMode = ht);
let $t = { type: "zoomIn", currentView: l, previousView: b, lastView: _, currentStage: this.currentStage, duration: p, ease: y }, Zt = async () => {
if (c && f && l) {
if (l.appendChild(f), this.prefetcher.scanAndPrefetch(l, a), typeof this.views[t] == "object" && typeof this.views[t].mounted == "function") try {
await this.views[t].mounted();
} catch (w) {
this.notify(`deferred view mounted() failed: ${w.message}`, "error");
}
if (this._destroyed) return;
this._emit("viewMounted", { viewName: t, node: l });
}
this._destroyed || (this.blockEvents = false, this._onTransitionComplete(), this._updateNav(), this._emit("afterZoomIn", { viewName: t, zoomLevel: this.zoomLevel() }), this.tracing("ended"));
};
await this._runNavigationTransition($t, Zt), this._destroyed && f && C(f);
}
async zoomIn(t) {
this._destroyed || t?.dataset?.to && await this._doZoomIn(t.dataset.to, { el: t });
}
async _doLateral(t, e = false, s = {}) {
return this._runNavigation(() => this._performLateral(t, e, s));
}
async _performLateral(t, e = false, s = {}) {
if (!this.isValid || !this.canvas) return;
if (!Object.prototype.hasOwnProperty.call(this.views, t)) {
this.notify(`goTo("${t}", { mode: 'lateral' }): view not found in views. Available: ${Object.keys(this.views).join(", ")}`, "warn");
return;
}
let i = this.canvas.querySelector(".is-current-view");
if (!i) return;
let n = i.dataset?.viewName;
if (n === t || (this._emit("beforeLateral", { viewName: t, from: n, isBack: e }), this._destroyed)) return;
let o = this.lateralNav && this.lateralNav.keepAlive, a = null;
if (!e) {
this.lateralHistory = this.lateralHistory || [];
let w = this.storedViews[this.storedViews.length - 1];
a = { name: n, stage: this._copyLateralStage(w), node: o ? i : null }, a.entry = a.stage.views[0];
}
this.tracing("lateral()");
let c = s.duration ?? this.duration, l = s.ease ?? this.ease, f = this.canvas.querySelector(".is-previous-view"), h = this.canvas.querySelector(".is-last-view"), u = null, d = null;
if (o && (e && s.keepAliveNode ? d = s.keepAliveNode : d = this.canvas.querySelector(`.is-lateral-hidden[data-view-name="${t}"]`)), d) {
if (d.style.display = "", d.style.opacity = "", d.classList.remove("is-lateral-hidden", "zoom-lateral-out"), d.classList.add("is-new-current-view", "has-no-events"), u = d, !e && this.lateralHistory) {
let w = this.lateralHistory.findIndex((L) => L.node === d);
w !== -1 && this.lateralHistory.splice(w, 1);
}
} else {
let w = () => {
e && this.lateralHistory.push({ name: t, entry: s.savedEntry, stage: s.savedStage, node: s.keepAliveNode ?? null });
}, L = this._createViewContext(s.props ?? {});
try {
let X = await this.prefetcher.get(t, L);
if (this._destroyed) {
C(X);
return;
}
this.prefetcher.scanAndPrefetch(X, L), u = await ut(X, t, this.canvas, false, this.views, this.componentContext);
} catch (X) {
this.notify(`lateral navigation aborted: failed to resolve view "${t}": ${X.message}`, "error"), w();
return;
}
if (!u) {
w();
return;
}
if (this._destroyed) {
C(u), u.remove();
return;
}
if (this._emit("viewMounted", { viewName: t, node: u }), this._destroyed) return;
}
let v = i.style.transform || "", m = this.storedViews[this.storedViews.length - 1];
u.style.transform = "", u.style.transformOrigin = "0 0";
let p = /* @__PURE__ */ new Map(), y = (w) => (p.has(w) || p.set(w, w.getBoundingClientRect()), p.get(w)), g = y(u), x = y(i), b = y(this.canvas), _ = b.width / this.canvas.offsetWidth || 1, S = b.height / this.canvas.offsetHeight || 1, O = f ? f.querySelectorAll(".zoom-me[data-to]") : [], A, E;
for (let w of O) if (!A && w.dataset.to === n && (A = w), !E && w.dataset.to === t && (E = w), A && E) break;
let T = 0, P = 0, B = this._declaredSiblings(), z = B ? B.indexOf(n) : -1, D = B ? B.indexOf(t) : -1;
if (z !== -1 && D !== -1) T = (z - D) * b.width * 0.15;
else if (f) if (A && E) {
let w = y(A), L = y(E);
T = w.left + w.width / 2 - (L.left + L.width / 2), P = w.top + w.height / 2 - (L.top + L.height / 2);
} else T = b.width * 0.15;
let N = f ? { transformStart: f.style.transform || "", transformEnd: this._computeLateralBackTransform(f.style.transform || "", T, P) } : null, I = h && m.views[2] ? { transformStart: h.style.transform || "", transformEnd: this._computeLateralBackTransform(h.style.transform || "", T * 0.7, P * 0.7) } : null, F = m.views[0], M = this._computeLateralBackTransform("", (x.x - g.x + (x.width - g.width) / 2) / _, (x.y - g.y + (x.height - g.height) / 2) / S), wt = Y(F.backwardState.transform || "").scale, ft = this._computeLateralBackTransform(F.backwardState.transform || "", (x.width - g.width) * wt / (2 * _), (x.height - g.height) * wt / (2 * S)), U = f?.querySelector(".zoomed");
if (E && N && !(e && s.savedStage)) {
let w = this._lateralTargetGeometry(u, E, f, h, m, { measure: y, outgoingView: i, canvasRect: b, sx: _, sy: S });
w && (M = w.currentEnd, ft = w.currentBack, N.transformEnd = w.previousEnd, I && w.lastEnd && (I.transformEnd = w.lastEnd), m.scale = w.scale);
}
if (e && s.savedStage) {
Object.assign(m, this._copyLateralStage(s.savedStage)), M = m.views[0].forwardState?.transform || "", ft = m.views[0].backwardState.transform, N && (N.transformEnd = m.views[1].forwardState.transform), I && (I.transformEnd = m.views[2].forwardState.transform);
let w = et(N?.transformStart || v), L = et(N?.transformEnd || M);
T = L.tx - w.tx, P = L.ty - w.ty;
} else m.views[0] = mt(t, { origin: "0 0", duration: c, ease: l, transform: ft }, { origin: "0 0", duration: c, ease: l, transform: M });
this.currentStage = m, a && this.lateralHistory.push(a), U?.classList.remove("zoomed");
let W = E ? this._resolveHideTrigger(E) : false;
m.hideTriggerMode = W, E && (E.classList.add("zoomed"), this._applyHideTrigger(E, u, W, c, l, false), W && E.classList.add("z-trigger-hidden")), Ct(u), N && m.views[1] && (m.views[1].forwardState = { ...m.views[1].forwardState, transform: N.transformEnd }), I && m.views[2] && (m.views[2].forwardState = { ...m.views[2].forwardState, transform: I.transformEnd });
let It = this._computeLateralBackTransform(M, -T, -P), ht = this._computeLateralBackTransform(v, T, P), $t = { type: "lateral", currentView: u, previousView: i, lastView: h || null, backView: f || null, backViewState: N, lastViewState: I, incomingTransformStart: It, incomingTransformEnd: M, outgoingTransform: v, outgoingTransformEnd: ht, currentStage: this.currentStage, duration: c, ease: l, canvas: this.canvas, slideDeltaX: T, slideDeltaY: P, keepAlive: o && !e ? o : false }, Zt = () => {
U && U !== E && (U.classList.remove("z-trigger-hidden", "z-trigger-fade", "z-trigger-fade-reverse"), U.style.removeProperty("--zoom-duration"), U.style.removeProperty("--zoom-ease")), W === "fade" && E.classList.remove("z-trigger-hidden"), o && !e && (i.classList.remove("is-current-view", "is-new-current-view", "has-no-events"), i.classList.add("is-lateral-hidden"), o !== "visible" ? i.style.display = "none" : (i.style.transform = v, i.style.opacity = "")), this.blockEvents = false, this._onTransitionComplete(), this._updateNav(), this._emit("afterLateral", { viewName: t, from: n, isBack: e }), this.tracing("ended");
};
return this._runNavigationTransition($t, Zt);
}
_copyLateralStage(t) {
return { ...t, views: t.views.map((e) => ({ ...e, ...e.backwardState && { backwardState: { ...e.backwardState } }, ...(e.forwardState || e.backwardState) && { forwardState: { ...e.forwardState || e.backwardState } } })) };
}
_lateralTargetGeometry(t, e, s, i, n, o) {
let { measure: a, outgoingView: c, canvasRect: l, sx: f, sy: h } = o, u = (z) => {
let D = a(z);
return { x: (D.x - l.x) / f, y: (D.y - l.y) / h, width: D.width / f, height: D.height / h };
}, d = u(t), v = u(c), m = u(s), p = u(e);
if (!d.width || !d.height || !p.width || !p.height) return null;
let y = Y(s.style.transform || ""), g = nt(s.style.transformOrigin || "0 0"), x = e.dataset.withCover || this.cover, b = St(p.width, p.height, d.width, d.height, x).scale, _ = y.scale * b, S = (z, D, N, I, F, M) => dt(z, D, N, I.tx, I.ty, I.scale, F, M.tx, M.ty, M.scale), O = S(p, m, g, y, g, { ...y, scale: _ }), A = { tx: y.tx + v.x + v.width / 2 - O.x - O.width / 2, ty: y.ty + v.y + v.height / 2 - O.y - O.height / 2, scale: _ }, E = (z) => `translate(${z.tx}px, ${z.ty}px) scale(${z.scale})`, T = S(p, m, g, y, { x: 0, y: 0 }, Y(n.views[1].backwardState.transform || "")), P = St(T.width, T.height, d.width, d.height, x).scaleInv, B = { currentEnd: `translate(${v.x - d.x + (v.width - d.width) / 2}px, ${v.y - d.y + (v.height - d.height) / 2}px)`, currentBack: Dt(T, { left: d.x, top: d.y }, d, P), previousEnd: E(A), scale: _ };
if (i && n.views[2]) {
let z = u(i), D = nt(i.style.transformOrigin || "0 0"), N = Y(i.style.transform || ""), I = S(m, m, g, y, g, A), F = { ...N, scale: N.scale * b }, M = S(z, z, D, N, D, F);
F.tx += I.x + b * (z.x - m.x) - M.x, F.ty += I.y + b * (z.y - m.y) - M.y, B.lastEnd = E(F);
}
return B;
}
_computeLateralBackTransform(t, e, s) {
let i = et(t);
return i.matched ? `translate(${i.tx + e}px, ${i.ty + s}px) ${i.rest}`.trim() : `translate(${e}px, ${s}px) ${t}`.trim();
}
zoomOut() {
return this._runNavigation(() => this._performZoomOut());
}
_performZoomOut() {
if (this._emit("beforeZoomOut", { zoomLevel: this.zoomLevel() }), this._destroyed) return;
this.tracing("zoomOut()");
let t = this.canvas;
this._resetCanvasScroll(t);
let e = t.querySelector(".is-current-view"), s = t.querySelector(".is-previous-view");
if (!e || !s) {
this.notify("zoomOut: current or previous view not found (animation may still be running)", "warn");
return;
}
this._cleanupLateralKeepAlive(), this.lateralHistory = [], this.currentStage = this.storedViews[this.storedViews.length - 1];
let i = t.querySelector(".is-last-view");
this.tracing("setCSSVariables()");
let n = this.currentStage.views[0]?.forwardState?.duration ?? this.duration, o = this.currentStage.views[0]?.forwardState?.ease ?? this.ease, a = s.querySelector(".zoomed");
a && a.classList.remove("zoomed");
let c = this.currentStage.hideTriggerMode;
c && a && this._restoreHideTrigger(a, c, n, o), c === "fade" ? (e.classList.remove("z-view-fade-in"), e.style.setProperty("--zoom-duration", n), e.style.setProperty("--zoom-ease", o), e.classList.add("z-view-fade-out")) : (e.classList.remove("z-view-fade-in"), e.style.removeProperty("opacity")), this._removeEffect(s), s.classList.replace("is-previous-view", "is-current-view"), i !== null && (i.classList.contains("has-effect") && this.effects[0] !== "none" && i.style.setProperty("--z-effect-filter", this.effects[0]), i.classList.replace("is-last-view", "is-previous-view"), i.classList.remove("hide"), pt(i));
let l = Ft(this.currentStage);
if (l) {
t.prepend(l);
let u = t.querySelector(".z-view:first-child");
u && (pt(u), u.classList.add("hide"));
}
let f = { type: "zoomOut", currentView: e, previousView: s, lastView: i, currentStage: this.currentStage, duration: n, ease: o, canvas: t }, h = () => {
this.blockEvents = false, this._onTransitionComplete(), this.storedViews.pop(), this._updateNav(), this._emit("afterZoomOut", { zoomLevel: this.zoomLevel() }), this.tracing("ended");
};
return this._runNavigationTransition(f, h);
}
onZoom(t) {
if (this._destroyed || t.type === "mouseup" && t.button !== 0 || t.type === "click" && !this.inputs.keyboard || t.type === "mouseup" && !this.inputs.click || t.type === "touchend" && !this.inputs.touch) return;
let e = t.target;
if (e.closest?.("[inert]") || e.closest(".z-depth-nav") || e.closest(".z-lateral-nav")) return;
let s = e.classList.contains("zoom-me") || e.closest(".zoom-me");
if (!this.blockEvents && s && !this.touching) {
this.tracing("onZoom() \u2192 zoomIn"), t.preventDefault(), t.stopPropagation();
let i = e.classList.contains("zoom-me") ? e : e.closest(".zoom-me");
this.zoomIn(i);
return;
}
if (this.storedViews.length > 1 && !this.blockEvents && !s && !this.touching) {
let i = this.canvas.querySelector(".is-current-view");
if (i && i.contains(e)) return;
let n = this._findSiblingTriggerAtPoint(t.clientX, t.clientY);
if (n) {
this.tracing("onZoom() \u2192 lateral"), t.stopPropagation(), this._doLateral(n.dataset.to);
return;
}
this.tracing("onZoom() \u2192 zoomOut"), t.stopPropagation(), this.zoomOut();
}
}
_findSiblingTriggerAtPoint(t, e) {
let s = this.canvas.querySelector(".is-previous-view");
if (!s) return null;
let n = this.canvas.querySelector(".is-current-view")?.dataset?.viewName;
if (!n) return null;
let o = s.querySelectorAll(".zoom-me[data-to]");
for (let a of o) {
if (a.dataset.to === n) continue;
let c = a.getBoundingClientRect();
if (t >= c.left && t <= c.right && e >= c.top && e <= c.bottom) return a;
}
return null;
}
_getSiblings() {
let e = this.canvas.querySelector(".is-current-view")?.dataset?.viewName, s = this._declaredSiblings();
if (s && s.indexOf(e) !== -1) return { siblings: s, currentIndex: s.indexOf(e) };
let i = this.canvas.querySelector(".is-previous-view");
if (!i) return { siblings: [], currentIndex: -1 };
let n = i.querySelectorAll(".zoom-me[data-to]"), o = [];
for (let c of n) c.dataset.to && o.push(c.dataset.to);
let a = o.indexOf(e);
return { siblings: o, currentIndex: a };
}
_declaredSiblings() {
let t = this.lateralNav && typeof this.lateralNav == "object" ? this.lateralNav.siblings : null;
if (!t) return null;
if (Array.isArray(t)) return t;
let e = this.canvas.querySelector(".is-previous-view")?.dataset?.viewName;
return e && Array.isArray(t[e]) ? t[e] : null;
}
_updateNav() {
if (this._destroyed) return;
let t = this.storedViews.length - 1, e = this.canvas.querySelector(":scope > .z-view.is-current-view"), s = !!this.depthNav && t >= 1 && !e?.querySelector(".z-depth-nav"), i = this.lateralNav ? this._getSiblings() : null;
i?.siblings.length < 2 && (i = null), i && this.lateralNav.mode === "auto" && t >= 1 && e && e.offsetWidth >= this.canvas.offsetWidth && e.offsetHeight >= this.canvas.offsetHeight && (i = null), this._updateDepthNav(s), this._updateLateralNav(i);
}
_updateDepthNav(t) {
if (t === void 0) {
let n = this.canvas.querySelector(":scope > .z-view.is-current-view");
t = !!this.depthNav && this.storedViews.length > 1 && !n?.querySelector(".z-depth-nav");
}
if (!t) {
this._depthNavElement?.remove();
return;
}
let e = this.depthNav.position || "bottom-left", s = this._depthNavElement;
if (!s) {
s = document.createElement("div");
let n = document.createElement("button");
n.className = "z-nav-back", n.setAttribute("aria-label", "Zoom out (go back)"), n.innerHTML = "‹", n.type = "button", n.addEventListener("click", (o) => {
o.stopPropagation(), !this._destroyed && s.parentNode === this.canvas && !this.blockEvents && this.storedViews.length > 1 && this.zoomOut();
}), s.appendChild(n), this._depthNavElement = s;
}
let i = "z-depth-nav z-depth-nav--" + e;
s.className !== i && (s.className = i), s.parentNode !== this.canvas && this.canvas.appendChild(s);
}
_updateLateralNav(t) {
if (t === void 0 && (t = this.lateralNav ? this._getSiblings() : null, t?.siblings.length < 2 && (t = null), t && this.lateralNav.mode === "auto" && this.storedViews.length > 1)) {
let l = this.canvas.querySelector(":scope > .z-view.is-current-view");
l && l.offsetWidth >= this.canvas.offsetWidth && l.offsetHeight >= this.canvas.offsetHeight && (t = null);
}
if (!t) {
this._lateralNavState?.element.remove();
return;
}
let { siblings: e, currentIndex: s } = t, i = this.lateralNav.position || "bottom-center", n = !!this.lateralNav.arrows, o = !!this.lateralNav.dots, a = this._lateralNavState;
if (!a || a.arrows !== n || a.dots !== o || a.siblings.length !== e.length || a.siblings.some((l, f) => l !== e[f])) {
a?.element.remove();
let l = document.createElement("div");
a = { element: l, siblings: [...e], currentIndex: -1, arrows: n, dots: o, dotButtons: [] };
let f = (h, u, d) => {
let v = document.createElement("button");
return v.className = "z-nav-arrow z-nav-" + h, v.setAttribute("aria-label", u), v.innerHTML = d, v.type = "button", v;
};
if (n && (a.prevButton = f("prev", "Previous sibling view", "‹"), l.appendChild(a.prevButton)), o) {
let h = document.createElement("div");
h.className = "z-nav-lateral-dots";
for (let u of e) {
let d = document.createElement("button");
d.className = "z-nav-dot z-nav-lat-dot", d.setAttribute("aria-label", `Go to ${u}`), d.dataset.to = u, d.type = "button", a.dotButtons.push(d), h.appendChild(d);
}
l.appendChild(h);
}
n && (a.nextButton = f("next", "Next sibling view", "›"), l.appendChild(a.nextButton)), l.addEventListener("click", (h) => {
let u = h.target.closest?.("button");
if (!u || !l.contains(u)) return;
h.stopPropagation();
let d = this._lateralNavState;
if (this._destroyed || this.blockEvents || d?.element !== l || l.parentNode !== this.canvas) return;
let v = u === d.prevButton ? d.currentIndex - 1 : u === d.nextButton ? d.currentIndex + 1 : d.dotButtons.indexOf(u);
v >= 0 && v < d.siblings.length && v !== d.currentIndex && this._doLateral(d.siblings[v]);
}), this._lateralNavState = a;
}
if (a.currentIndex !== s) {
let l = a.dotButtons[a.currentIndex];
l?.classList.remove("is-active"), l?.removeAttribute("aria-current");
let f = a.dotButtons[s];
f?.classList.add("is-active"), f?.setAttribute("aria-current", "true"), a.currentIndex = s;
}
a.prevButton && a.prevButton.disabled !== s <= 0 && (a.prevButton.disabled = s <= 0), a.nextButton && a.nextButton.disabled !== s >= e.length - 1 && (a.nextButton.disabled = s >= e.length - 1);
let c = "z-lateral-nav z-lateral-nav--" + i;
a.element.className !== c && (a.element.className = c), a.element.parentNode !== this.canvas && this.canvas.appendChild(a.element);
}
_removeNav() {
this._depthNavElement?.remove(), this._lateralNavState?.element.remove(), this._depthNavElement = null, this._lateralNavState = null;
}
_cleanupLateralKeepAlive() {
if (!this.canvas) return;
let t = this.canvas.querySelectorAll(".is-lateral-hidden");
for (let e of t) this._accessibility.restore(e), C(e), e.remove();
}
onKeyDown(t) {
if (this._destroyed || !this.inputs.keyboard || t.defaultPrevented || Vt(t.target)) return;
let e = t.target.closest?.(".zoom-me[data-to]");
!e || kt(e) || e.closest("[inert]") || (t.key === " " || t.key === "Enter") && (t.preventDefault(), t.key === "Enter" && !t.repeat && !this.blockEvents && this.zoomIn(e));
}
onKeyUp(t) {
if (this._destroyed || !this.inputs.keyboard || t.defaultPrevented || Vt(t.target) || t.target.closest?.("[inert]")) return;
let e = t.target.closest?.(".zoom-me[data-to]");
if (t.key === " " && e && !kt(e)) {
t.preventDefault(), this.blockEvents || this.zoomIn(e);
return;
}
this.tracing("onKeyUp()"), (t.key === "ArrowLeft" || t.key === "ArrowDown") && (t.preventDefault(), this.storedViews.length > 1 && !this.blockEvents ? this.zoomOut() : this.notify(`is on level zero. Can't zoom out. Trigger: ${t.key}`, "warn"));
}
onWheel(t) {
this._destroyed || this.inputs.wheel && (this._isInsideScrollable(t.target, t.deltaY) || (t.preventDefault(), !(this.blockEvents || this._wheelCooldown) && t.deltaY > 0 && this.storedViews.length > 1 && (this.tracing("onWheel() \u2192 zoomOut"), this._wheelCooldown = true, this._setTrackedTimeout(() => {
this._wheelCooldown = false;
}, 500), this.zoomOut())));
}
_isInsideScrollable(t, e) {
let s = t, i = this.canvas.querySelector(".is-current-view");
for (; s && s !== this.canvas; ) {
if (s === i || i && i.contains(s)) {
let { overflowY: n } = window.getComputedStyle(s);
if ((n === "auto" || n === "scroll") && (e > 0 ? s.scrollTop + s.clientHeight < s.scrollHeight - 1 : s.scrollTop > 0)) return true;
}
s = s.parentElement;
}
return false;
}
onTouchStart(t) {
this._destroyed || this.inputs.touch && (this.tracing("onTouchStart()"), this.touching = true, this.touchstartX = t.changedTouches[0].screenX, this.touchstartY = t.changedTouches[0].screenY);
}
onTouchEnd(t) {
this._destroyed || this.inputs.touch && (this.blockEvents || (this.tracing("onTouchEnd()"), this.touchendX = t.changedTouches[0].screenX, this.touchendY = t.changedTouches[0].screenY, this.handleGesture(t)), this.touching = false);
}
handleGesture(t) {
if (t.target.closest?.(".z-depth-nav, .z-lateral-nav")) return;
t.stopPropagation(), this.tracing("handleGesture()");
let e = this.touchendX - this.touchstartX, s = this.touchendY - this.touchstartY, i = Math.abs(e) < 10 && Math.abs(s) < 10;
e < -30 && (this.storedViews.length > 1 && !this.blockEvents ? (this.tracing("swipe left"), this.zoomOut()) : this.notify("is on level zero. Can't zoom out. Trigger: Swipe left", "warn"));
let n = t.target.classList.contains("zoom-me") ? t.target : t.target.closest(".zoom-me");
i && !this.blockEvents && n && this.touching && (this.touching = false, this.tracing("tap"), t.preventDefault(), this.zoomIn(n)), i && this.storedViews.length > 1 && !this.blockEvents && !n && t.target.closest(".is-current-view") === null && this.touching && (this.touching = false, this.tracing("tap"), this.zoomOut());
}
};
var cs = { separator: "/", prefix: "/" };
function qt(r, t) {
let e = [];
for (let s of r.storedViews) {
let i = s.views && s.views[0];
i && i.viewName && e.push(i.viewName);
}
return e.join(t);
}
function fs(r, t) {
let e = window.location.hash.slice(1);
return e.startsWith(r) && (e = e.slice(r.length)), e ? e.split(t).filter(Boolean) : [];
}
var Ze = { install(r, t) {
let e = Object.assign({}, cs, t), s = e.separator, i = e.prefix, n = false, o = false, a = null, c = false;
function l(v) {
if (n) return;
let m = qt(r, s), p = "#" + i + m;
window.location.hash !== p && (n = true, window.history[v](null, "", p), n = false);
}
function f() {
l("pushState");
}
function h() {
l("replaceState");
}
r.on("afterZoomIn", f), r.on("afterLateral", f), r.on("afterZoomOut", h);
async function u() {
if (!(o || c)) {
o = true, n = true;
try {
for (; a && !c; ) {
let v = a;
if (a = null, await r._navigationTask?.promise, c) return;
if (a) continue;
let m = qt(r, s).split(s).filter(Boolean);
if (v.join(s) !== m.join(s)) {
if (v.length > m.length) {
window.history.back();
continue;
}
for (; m.length > v.length && m.length > 1; ) {
let p = m.length;
if (await r.zoomOut(), c) return;
if (m = qt(r, s).split(s).filter(Boolean), a || m.length >= p) break;
}
if (!a && v.length === m.length && v.length > 0) {
let p = v[v.length - 1];
if (p !== m[m.length - 1]) {
let y = r.lateralHistory || [], g = (b) => typeof b == "object" ? b?.name : b, x = y.map(g).lastIndexOf(p);
if (x !== -1) for (; r.lateralHistory.length > x; ) {
let b = r.lateralHistory.length;
if (await r.back(), c || a || r.lateralHistory.length >= b) break;
}
else await r.goTo(p, { mode: "lateral" });
}
}
}
}
} finally {
n = false, o = false;
}
}
}
function d() {
c || (a = fs(i, s), u());
}
window.addEventListener("popstate", d), h(), r.on("destroy", function() {
c = true, a = null, r.off("afterZoomIn", f), r.off("afterLateral", f), r.off("afterZoomOut", h), window.removeEventListener("popstate", d);
});
} };
gt.Router = Ze;
// src/components/sizes.js
var SIZE_RATIOS = Object.freeze({
xxl: 1,
xl: 260 / 420,
l: 160 / 420,
m: 100 / 420,
s: 62 / 420,
xs: 38 / 420,
xxs: 16 / 420
});
var SIZES = Object.freeze(Object.keys(SIZE_RATIOS));
var aliases = Object.freeze({
extralarge: "xl",
large: "l",
medium: "m",
small: "s",
extrasmall: "xs"
});
function normaliseSize(value, fallback = "m") {
const key = String(value ?? "").trim().toLowerCase();
const size = aliases[key] || key;
return SIZES.includes(size) ? size : SIZES.includes(fallback) ? fallback : "m";
}
function sizeVariable(value, fallback = "m") {
return `var(--z-size-${normaliseSize(value, fallback)})`;
}
// src/components/surface.js
var HTMLElementBase = globalThis.HTMLElement || class {
};
var own = (node, key) => Object.prototype.hasOwnProperty.call(node, key);
var booleanAttributes = /* @__PURE__ */ new Set(["circle", "square", "button", "disabled", "slider", "knob"]);
var attributes = [
"size",
"distance",
"angle",
"circle",
"square",
"label",
"label-pos",
"image-path",
"to-view",
"button",
"disabled",
"slider",
"progress",
"knob",
"qty",
"unit",
"min",
"max",
"step",
"pos"
];
var defaults = { distance: 100, angle: 0, progress: 0, qty: 0, min: 0, max: 100, step: 1 };
var properties = Object.fromEntries(attributes.map((attribute) => [
attribute.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()),
attribute
]));
function numberAttribute(element, name, fallback) {
const text = element.getAttribute(name);
const value = text === null || text.trim() === "" ? fallback : Number(text);
return Number.isFinite(value) ? value : fallback;
}
function part(parent, name, tag = "div", className = "") {
let node = Array.from(parent.children).find((child) => child.dataset.zPart === name);
if (!node) {
node = parent.ownerDocument.createElement(tag);
node.dataset.zPart = name;
if (className) node.className = className;
parent.append(node);
}
return node;
}
function isDefinition(element) {
const view = element.localName === "z-view" ? element : element.closest("z-view");
return view?.hasAttribute("name") && !view.hasAttribute("data-view-name") && view.parentElement?.localName === "z-canvas";
}
function logicalParent(element) {
return element.parentElement?.closest("z-view, z-spot, z-list") || null;
}
function setAttribute(element, name, value) {
if (element.getAttribute(name) !== String(value)) element.setAttribute(name, String(value));
}
var ZSurface = class _ZSurface extends HTMLElementBase {
static get observedAttributes() {
return attributes;
}
constructor() {
super();
this._surfaceUpdating = false;
this._surfaceQueued = false;
this._surfaceListening = false;
this._surfaceParts = null;
this._surfaceObserver = null;
this._surfaceResizeObserver = null;
this._onSurfaceAction = (event) => this._handleAction(event);
this._onSurfaceDisabled = (event) => {
if (this.hasAttribute("disabled") && event.target.closest?.("z-spot") === this) {
event.preventDefault();
event.stopImmediatePropagation();
}
};
this._onSurfaceKey = (event) => this._handleKey(event);
this._onSurfaceValue = (event) => this._handleValue(event);
this._onSurfaceScroll = () => this._syncScrollValue();
}
connectedCallback() {
if (isDefinition(this)) return;
this.refresh();
}
disconnectedCallback() {
queueMicrotask(() => {
if (this.isConnected) return;
this._surfaceObserver?.disconnect();
this._surfaceResizeObserver?.disconnect();
this._unlisten();
});
}
attributeChangedCallback() {
if (this.isConnected && !isDefinition(this)) this.refresh();
}
get contentElement() {
return this._surfaceParts?.content || null;
}
refresh() {
if (this._surfaceUpdating || !this.isConnected || isDefinition(this)) return this;
this._surfaceUpdating = true;
this._surfaceObserver?.disconnect();
try {
for (const property of Object.keys(properties)) {
if (own(this, property)) {
const value = this[property];
delete this[property];
this[property] = value;
}
}
this.classList.add("z-surface", `z-${this.surfaceType}`);
if (this.surfaceType === "spot") this.classList.add("satellite");
const body = part(this, "surface", "div", "z-surface-body");
this._surfaceParts = {
body,
plate: part(this, "plate", "div", "z-plate"),
image: part(body, "image", "div", "z-image"),
content: part(body, "content", "div", "z-content"),
media: part(body, "media", "div", "z-media"),
extensions: part(this, "extensions", "div", "z-extensions")
};
this._adoptContent();
this._syncAppearance();
this._syncControls();
this._syncPosition();
this._syncScroll();
this._listen();
} finally {
this._surfaceUpdating = false;
this._observe();
}
return this;
}
_adoptContent() {
const slots = this._surfaceParts;
const authored = [this, slots.content, slots.image, slots.media, slots.extensions].flatMap((holder) => Array.from(holder.childNodes));
for (const node of authored) {
if (node.nodeType === 1 && node.hasAttribute("data-z-part")) continue;
const slot = node.nodeType === 1 ? node.getAttribute("slot") : null;
const radial = node.nodeType === 1 && node.matches("z-spot, z-list");
const target = slot === "image" ? slots.image : slot === "media" ? slots.media : slot === "extension" || radial ? slots.extensions : slots.content;
if (node.parentNode !== target) target.append(node);
}
for (const node of Array.from(slots.extensions.children)) {
if (node.localName === "z-spot") this._adoptSpot(node);
}
}
_adoptSpot(spot) {
const extensions = this._surfaceParts?.extensions;
if (!extensions || spot === this) return;
const layout = part(extensions, "layout", "div", "bigbang z-orbit-layout");
const gravity = part(layout, "gravity", "div", "gravity-spot");
let ring = spot.parentElement;
if (ring?.dataset.zPart !== "spot-orbit" || ring.parentElement !== gravity) {
ring = this.ownerDocument.createElement("div");
ring.className = "orbit-12 z-spot-orbit";
ring.dataset.zPart = "spot-orbit";
gravity.append(ring);
ring.append(spot);
}
this._sizeSpotOrbit(spot, ring);
}
_sizeSpotOrbit(spot, ring) {
const size = sizeVariable(this.getAttribute("size"), this.defaultSize);
const distance = numberAttribute(spot, "distance", 100);
ring.style.setProperty("--o-force", `calc(${size} * ${distance / 100})`);
ring.style.setProperty("--o-force-ratio", "1");
ring.style.setProperty("--o-range", "0deg");
}
_syncPosition() {
const size = normaliseSize(this.getAttribute("size"), this.defaultSize);
this.style.setProperty("--z-diameter", sizeVariable(size));
this.dataset.zSize = size;
if (this.surfaceType === "spot") {
this.style.setProperty("--o-from", `${numberAttribute(this, "angle", 0)}deg`);
this.style.setProperty("--o-offset", "0deg");
const parent = logicalParent(this);
if (parent instanceof _ZSurface) parent._adoptSpot(this);
}
const gravity = this._surfaceParts.extensions.querySelector(':scope > [data-z-part="layout"] > [data-z-part="gravity"]');
if (gravity) {
for (const ring of gravity.children) {
if (ring.dataset.zPart !== "spot-orbit") continue;
const spot = Array.from(ring.children).find((child) => child.localName === "z-spot");
if (spot) this._sizeSpotOrbit(spot, ring);
else ring.remove();
}
}
}
_syncAppearance() {
const square = this.hasAttribute("square");
this.classList.toggle("is-square", square);
this.classList.toggle("is-circle", !square);
this.classList.toggle("z-button", this.hasAttribute("button"));
const disabled = this.hasAttribute("disabled");
this.classList.toggle("is-disabled", disabled);
const target = this.getAttribute("to-view");
const navigable = this.surfaceType === "spot" && Boolean(target) && !disabled;
this.classList.toggle("zoom-me", navigable);
if (navigable) this.dataset.to = target;
else delete this.dataset.to;
if (this.surfaceType === "spot") {
const button = Boolean(target) || this.hasAttribute("button");
if (button) {
setAttribute(this, "role", "button");
setAttribute(this, "tabindex", disabled ? "-1" : "0");
setAttribute(this, "aria-disabled", disabled ? "true" : "false");
this.dataset.zInteractive = "";
} else if (this.hasAttribute("data-z-interactive")) {
this.removeAttribute("role");
this.removeAttribute("tabindex");
this.removeAttribute("aria-disabled");
delete this.dataset.zInteractive;
}
}
const image = this._surfaceParts.image;
let source = Array.from(image.children).find((node) => node.dataset.zPart === "image-source");
const path = this.getAttribute("image-path");
if (path) {
source ||= part(image, "image-source", "img", "z-image-source");
setAttribute(source, "src", path);
setAttribute(source, "alt", "");
} else source?.remove();
image.hidden = !image.childNodes.length;
this._surfaceParts.media.hidden = !this._surfaceParts.media.childNodes.length;
const labelText = this.getAttribute("label") || "";
const quantityOutside = this.getAttribute("pos") === "outside" && this.hasAttribute("qty") && !this.hasAttribute("knob");
let label = Array.from(this.children).find((node) => node.dataset.zPart === "label");
if (labelText || quantityOutside) {
label ||= part(this, "label", "div", "z-label");
const position = this.getAttribute("label-pos") || "bottom";
for (const token of ["top", "bottom", "left", "right", "center"]) label.classList.toggle(token, token === position);
label.dataset.position = position;
const inside = part(label, "label-text", "span", "inside");
if (inside.textContent !== labelText) inside.textContent = labelText;
} else label?.remove();
let quantity = this.querySelector(':scope > [data-z-part="label"] > [data-z-part="quantity"], :scope > [data-z-part="surface"] > [data-z-part="content"] > [data-z-part="quantity"]');
if (this.hasAttribute("qty") && !this.hasAttribute("knob")) {
const holder = quantityOutside ? label : this._surfaceParts.content;
if (!quantity) quantity = part(holder, "quantity", "span", "z-quantity");
else if (quantity.parentElement !== holder) holder.append(quantity);
const text = `${numberAttribute(this, "qty", 0)}${this.getAttribute("unit") || ""}`;
if (quantity.textContent !== text) quantity.textContent = text;
} else quantity?.remove();
}
_syncControls() {
for (const [flag, tag, className, forwarded] of [
["slider", "z-slider", "z-surface-slider", ["progress"]],
["knob", "z-knob", "z-surface-knob", ["qty", "unit", "min", "max", "step", "disabled"]]
]) {
let control = Array.from(this.children).find((node) => node.dataset.zPart === flag);
if (this.hasAttribute(flag) && (flag !== "slider" || !this.hasAttribute("square"))) {
control ||= part(this, flag, tag, className);
for (const attribute of forwarded) {
if (this.hasAttribute(attribute)) setAttribute(control, attribute, this.getAttribute(attribute));
else control.removeAttribute(attribute);
}
setAttribute(control, "aria-label", this.getAttribute("aria-label") || this.getAttribute("label") || (flag === "knob" ? "Value" : "Progress"));
} else control?.remove();
}
this._surfaceParts.content.hidden = this.hasAttribute("knob");
}
_syncScroll() {
if (this.surfaceType !== "view") return;
const content = this._surfaceParts.content;
const square = this.hasAttribute("square") || !this.hasAttribute("circle") && this.closest("[data-shape]")?.dataset.shape === "square";
const overflowing = !square && content.clientHeight > 0 && content.scrollHeight > content.clientHeight + 1;
let control = Array.from(this.children).find((node) => node.dataset.zPart === "scroll");
if (overflowing) {
control ||= part(this, "scroll", "z-scroll", "z-surface-scroll");
setAttribute(control, "aria-label", "Scroll content");
this._syncScrollValue();
} else control?.remove();
}
_syncScrollValue() {
const control = Array.from(this.children).find((node) => node.dataset.zPart === "scroll");
if (!control || !this._surfaceParts) return;
const content = this._surfaceParts.content;
const range = content.scrollHeight - content.clientHeight;
setAttribute(control, "scroll-val", range > 0 ? -45 + 90 * content.scrollTop / range : -45);
}
_listen() {
if (this._surfaceListening) return;
this._surfaceListening = true;
for (const type of ["mouseup", "touchend", "click"]) {
this.addEventListener(type, this._onSurfaceAction);
this.addEventListener(type, this._onSurfaceDisabled, true);
}
this.addEventListener("keydown", this._onSurfaceKey);
this.addEventListener("input", this._onSurfaceValue, true);
this.addEventListener("change", this._onSurfaceValue, true);
this._surfaceParts.content.addEventListener("scroll", this._onSurfaceScroll, { passive: true });
}
_unlisten() {
if (!this._surfaceListening) return;
this._surfaceListening = false;
for (const type of ["mouseup", "touchend", "click"]) {
this.removeEventListener(type, this._onSurfaceAction);
this.removeEventListener(type, this._onSurfaceDisabled, true);
}
this.removeEventListener("keydown", this._onSurfaceKey);
this.removeEventListener("input", this._onSurfaceValue, true);
this.removeEventListener("change", this._onSurfaceValue, true);
this._surfaceParts?.content.removeEventListener("scroll", this._onSurfaceScroll);
}
_handleAction(event) {
if (this.surfaceType !== "spot") return;
if (event.target.closest?.("z-spot") !== this) return;
const control = event.target.closest?.("button, a, input, select, textarea, z-knob, z-scroll");
if (event.type === "click" && !this.getAttribute("to-view") && !this.parentElement?.closest(".zoom-me")) return;
if (this.hasAttribute("disabled") || !this.getAttribute("to-view") || control && control !== this) {
event.stopPropagation();
if (this.hasAttribute("disabled")) event.preventDefault();
}
}
_handleKey(event) {
if (this.surfaceType !== "spot" || event.target !== this) return;
if (this.hasAttribute("disabled")) {
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
event.stopPropagation();
}
return;
}
if (this.getAttribute("to-view") || !this.hasAttribute("button")) return;
if ((event.key === "Enter" || event.key === " ") && !event.repeat) {
event.preventDefault();
event.stopPropagation();
this.click();
}
}
_handleValue(event) {
if (event.target.parentElement === this && event.target.dataset.zPart === "scroll") {
const value = Number(event.detail?.scrollVal ?? event.detail?.value);
if (Number.isFinite(value)) {
const content = this._surfaceParts.content;
content.scrollTop = (Math.min(45, Math.max(-45, value)) + 45) / 90 * Math.max(0, content.scrollHeight - content.clientHeight);
}
event.stopPropagation();
return;
}
if (event.target.parentElement !== this || event.target.dataset.zPart !== "knob") return;
const qty = event.detail?.qty ?? event.detail?.value;
if (!Number.isFinite(Number(qty))) return;
setAttribute(this, "qty", Number(qty));
event.stopImmediatePropagation();
this.dispatchEvent(new CustomEvent(event.type, {
detail: { ...event.detail, qty: Number(qty), value: Number(qty) },
bubbles: true,
composed: true
}));
}
_observe() {
if (!this.isConnected) return;
this._surfaceObserver ||= new MutationObserver((records) => {
const holders = [
this,
this._surfaceParts?.extensions,
this._surfaceParts?.content,
this._surfaceParts?.image,
this._surfaceParts?.media
];
if (records.some((record) => record.type === "attributes" || holders.includes(record.target) && record.addedNodes.length || this._surfaceParts?.content.contains(record.target))) this._queueRefresh();
});
this._surfaceObserver.observe(this, { childList: true, subtree: true, characterData: true, attributes: true, attributeFilter: ["slot"] });
if (this.surfaceType === "view") {
this._surfaceResizeObserver ||= new ResizeObserver(() => this._syncScroll());
this._surfaceResizeObserver.disconnect();
this._surfaceResizeObserver.observe(this._surfaceParts.content);
for (const child of this._surfaceParts.content.children) this._surfaceResizeObserver.observe(child);
}
}
_queueRefresh() {
if (this._surfaceQueued) return;
this._surfaceQueued = true;
queueMicrotask(() => {
this._surfaceQueued = false;
this.refresh();
});
}
};
for (const [property, attribute] of Object.entries(properties)) {
Object.defineProperty(ZSurface.prototype, property, {
configurable: true,
get() {
if (booleanAttributes.has(attribute)) return this.hasAttribute(attribute);
if (own(defaults, attribute)) return numberAttribute(
this,
attribute,
attribute === "distance" && this.surfaceType === "view" ? 0 : defaults[attribute]
);
if (attribute === "size") return normaliseSize(this.getAttribute(attribute), this.defaultSize);
return this.getAttribute(attribute) ?? "";
},
set(value) {
if (booleanAttributes.has(attribute)) this.toggleAttribute(attribute, Boolean(value));
else if (value === null || value === void 0) this.removeAttribute(attribute);
else setAttribute(this, attribute, value);
}
});
}
function hydrateSurfaces(root) {
if (!root) return;
if (root instanceof ZSurface) root.refresh();
for (const surface of root.querySelectorAll?.("z-view, z-spot") || []) {
if (surface instanceof ZSurface) surface.refresh();
}
}
// src/core.js
var THEMES = Object.freeze(["white", "light-blue", "black", "purple", "orange", "yellow", "blue", "green", "red", "gray"]);
var MODES = Object.freeze(["light", "light-filled", "dark", "dark-filled"]);
var instances = /* @__PURE__ */ new WeakMap();
var sequence = 0;
function choice(value, allowed, label) {
if (!allowed.includes(value)) throw new TypeError(`Zircle: unknown ${label} "${value}". Use ${allowed.join(", ")}.`);
return value;
}
async function createZircle(options = {}) {
if (typeof document === "undefined") throw new Error("Zircle: createZircle() needs a browser. Call it after mounting your component.");
if (options.signal?.aborted) throw new DOMException("Zircle initialization was cancelled.", "AbortError");
const mount = typeof options.mount === "string" ? document.querySelector(options.mount) : options.mount;
if (!(mount instanceof HTMLElement) || !mount.isConnected) throw new Error("Zircle: mount must be a connected HTML element or a matching selector.");
if (mount.getRootNode() !== document) throw new Error("Zircle: Zumly requires a mount in the document light DOM.");
if (instances.has(mount)) throw new Error("Zircle: this element already has an instance. Destroy it before mounting again.");
const views = options.views;
if (!views || typeof views !== "object" || !Object.keys(views).length) throw new TypeError("Zircle: provide a nonempty views map.");
const initialView = options.initialView ?? Object.keys(views)[0];
if (!Object.hasOwn(views, initialView)) throw new Error(`Zircle: initial view "${initialView}" is not registered.`);
let theme = choice(options.theme ?? "black", THEMES, "theme");
let mode = choice(options.mode ?? "dark", MODES, "mode");
let shape = choice(options.shape ?? "circle", ["circle", "square"], "shape");
let destroyed = false;
let abortListener;
const stage = document.createElement("div");
stage.className = "zircle z-stage";
const canvas = document.createElement("div");
canvas.className = "zircle zumly-canvas";
canvas.dataset.zircleInstance = String(++sequence);
canvas.setAttribute("aria-label", options.label ?? "Zircle");
const applyStyle = () => {
canvas.dataset.theme = theme;
canvas.dataset.mode = mode;
canvas.dataset.shape = shape;
stage.dataset.theme = theme;
stage.dataset.mode = mode;
stage.dataset.shape = shape;
};
applyStyle();
const originalPosition = mount.style.position;
const needsPosition = getComputedStyle(mount).position === "static";
if (needsPosition) mount.style.position = "relative";
stage.append(canvas);
mount.append(stage);
const resize = () => {
const { width, height } = mount.getBoundingClientRect();
const side = Math.max(1, Math.min(840, width, height));
canvas.style.width = `${side}px`;
canvas.style.height = `${side}px`;
canvas.style.left = `${(width - side) / 2}px`;
canvas.style.top = `${(height - side) / 2}px`;
const diameter = side * 0.5;
for (const [size, ratio] of Object.entries({ xxl: 1, xl: 260 / 420, l: 160 / 420, m: 100 / 420, s: 62 / 420, xs: 38 / 420, xxs: 16 / 420 })) {
canvas.style.setProperty(`--z-size-${size}`, `${diameter * ratio}px`);
}
Orbit.refresh(canvas);
};
resize();
const observer = new ResizeObserver(resize);
observer.observe(mount);
const app = new gt({
mount: `[data-zircle-instance="${sequence}"]`,
initialView,
views,
transitions: { driver: "css", duration: "700ms", ease: "ease-in-out", cover: "width", effects: ["blur(1px) opacity(0.35)", "blur(3px) opacity(0.12)"], hideTrigger: false, ...options.transitions },
depthNav: false,
lateralNav: false,
inputs: { click: true, touch: true, keyboard: true, wheel: false, ...options.inputs },
debug: options.debug ?? false,
componentContext: options.context ?? /* @__PURE__ */ new Map(),
preload: options.preload,
deferred: false
});
const events = new EventTarget();
const emit2 = (type, detail) => {
events.dispatchEvent(new CustomEvent(type, { detail }));
mount.dispatchEvent(new CustomEvent(`zircle:${type}`, { detail, bubbles: true }));
};
const refresh = () => {
hydrateSurfaces(canvas);
resize();
};
app.on("viewMounted", ({ node, viewName }) => {
hydrateSurfaces(node);
Orbit.refresh(node);
if (node.classList.contains("is-current-view") && !node.style.transform) {
node.style.transform = `translate(${(canvas.clientWidth - node.offsetWidth) / 2}px, ${(canvas.clientHeight - node.offsetHeight) / 2}px)`;
}
emit2("viewmount", { view: viewName, node });
});
const back = document.createElement("button");
back.type = "button";
back.className = "z-back z-depth-nav";
back.textContent = "\u2190";
back.setAttribute("aria-label", options.backLabel ?? "Go back");
back.hidden = true;
back.addEventListener("click", () => api.back());
const notifyNavigation = () => {
back.hidden = app.zoomLevel() <= 1;
if (options.backButton !== false) canvas.append(back);
emit2("viewchange", { view: app.getCurrentViewName(), depth: Math.max(0, app.zoomLevel() - 1) });
};
for (const event of ["afterZoomIn", "afterZoomOut", "afterLateral"]) app.on(event, notifyNavigation);
if (options.router) app.use(Ze, typeof options.router === "object" ? options.router : void 0);
const validateTarget = (name) => {
if (destroyed) throw new Error("Zircle: this instance has been destroyed.");
if (!Object.hasOwn(views, name)) throw new Error(`Zircle: view "${name}" is not registered.`);
};
const api = {
app,
canvas,
mount,
getCurrentViewName: () => app.getCurrentViewName(),
getHistory: () => app.storedViews.map((stage2) => stage2.views[0].viewName),
getHistoryLength: () => app.storedViews.length,
getTheme: () => theme,
getMode: () => mode,
getShape: () => shape,
setTheme(value) {
theme = choice(value, THEMES, "theme");
applyStyle();
emit2("stylechange", { theme, mode, shape });
return api;
},
setMode(value) {
mode = choice(value, MODES, "mode");
applyStyle();
emit2("stylechange", { theme, mode, shape });
return api;
},
setShape(value) {
shape = choice(value, ["circle", "square"], "shape");
applyStyle();
hydrateSurfaces(canvas);
emit2("stylechange", { theme, mode, shape });
return api;
},
async goTo(name, opts = {}) {
validateTarget(name);
await app.goTo(name, opts);
},
async zoomTo(name, opts = {}) {
validateTarget(name);
await app.zoomTo(name, opts);
},
async setView(target, opts = {}) {
const name = typeof target === "string" ? target : target?.name;
validateTarget(name);
await app.goTo(name, { ...opts, props: typeof target === "object" ? target.params ?? opts.props : opts.props });
},
async back() {
if (!destroyed) await app.back();
},
async zoomOut() {
if (!destroyed) await app.zoomOut();
},
goBack() {
return api.back();
},
refresh,
on(type, handler) {
events.addEventListener(type, handler);
return () => events.removeEventListener(type, handler);
},
destroy() {
if (destroyed) return;
destroyed = true;
if (abortListener) options.signal.removeEventListener("abort", abortListener);
observer.disconnect();
app.destroy();
stage.remove();
if (needsPosition && mount.style.position === "relative") mount.style.position = originalPosition;
instances.delete(mount);
emit2("destroy", {});
}
};
instances.set(mount, api);
if (options.signal) {
abortListener = () => api.destroy();
options.signal.addEventListener("abort", abortListener, { once: true });
}
try {
await app.init();
if (options.signal?.aborted) throw new DOMException("Zircle initialization was cancelled.", "AbortError");
if (!app.isValid || !app.getCurrentViewName()) throw new Error(`Zircle: could not render initial view "${initialView}".`);
refresh();
notifyNavigation();
emit2("ready", { instance: api });
return api;
} catch (error) {
api.destroy();
throw error;
}
}
// src/components/z-canvas.js
var HTMLElementBase2 = globalThis.HTMLElement ?? class {
};
var ZCanvas = class extends HTMLElementBase2 {
static observedAttributes = ["theme", "mode", "shape"];
connectedCallback() {
this._upgradeProperty("views");
this._upgradeProperty("options");
queueMicrotask(() => {
if (this.isConnected && !this._ready) this.init();
});
}
disconnectedCallback() {
queueMicrotask(() => {
if (!this.isConnected) this.destroy();
});
}
_upgradeProperty(name) {
if (Object.hasOwn(this, name)) {
const value = this[name];
delete this[name];
this[name] = value;
}
}
get ready() {
return this._ready ?? this.init();
}
get instance() {
return this._instance ?? null;
}
get views() {
return this._views;
}
set views(value) {
this._views = value;
}
get options() {
return this._options;
}
set options(value) {
this._options = value;
}
init() {
if (this._ready) return this._ready;
const generation = this._generation = (this._generation ?? 0) + 1;
this._controller = new AbortController();
const signal = this._controller.signal;
const views = { ...this._views };
let templateError;
for (const template2 of this.querySelectorAll(":scope > template[data-view]")) {
const name = template2.dataset.view;
if (Object.hasOwn(views, name)) {
templateError = new Error(`Zircle: duplicate view "${name}".`);
break;
}
if (!name.trim() || template2.content.children.length !== 1) {
templateError = new Error("Zircle: each view template needs a nonempty data-view name and exactly one root element.");
break;
}
views[name] = () => template2.content.firstElementChild.cloneNode(true);
}
this._ready = (templateError ? Promise.reject(templateError) : createZircle({
...this._options,
mount: this,
views,
signal,
initialView: this.getAttribute("initial-view") ?? this._options?.initialView ?? Object.keys(views)[0],
theme: this.getAttribute("theme") ?? this._options?.theme,
mode: this.getAttribute("mode") ?? this._options?.mode,
shape: this.getAttribute("shape") ?? this._options?.shape,
label: this.getAttribute("aria-label") ?? this._options?.label,
router: this.hasAttribute("router") || this._options?.router
})).then((instance) => {
if (generation !== this._generation || !this.isConnected) {
instance.destroy();
return null;
}
this._instance = instance;
for (const name of ["theme", "mode", "shape"]) this._applyStyleAttribute(name);
this.setAttribute("data-ready", "");
return instance;
}).catch((error) => {
if (signal.aborted) return null;
throw error;
});
this._ready.catch((error) => this.dispatchEvent(new CustomEvent("zircle:error", { detail: { error }, bubbles: true })));
return this._ready;
}
attributeChangedCallback(name, oldValue, value) {
if (oldValue === value || !this._instance) return;
this._applyStyleAttribute(name);
}
_applyStyleAttribute(name) {
const defaults2 = { theme: "black", mode: "dark", shape: "circle" };
const value = this.getAttribute(name) ?? this._options?.[name] ?? defaults2[name];
const suffix = `${name[0].toUpperCase()}${name.slice(1)}`;
if (this._instance[`get${suffix}`]() !== value) this._instance[`set${suffix}`](value);
}
async setView(name, options) {
return (await this.ready)?.setView(name, options);
}
async back() {
return (await this.ready)?.back();
}
destroy() {
this._generation = (this._generation ?? 0) + 1;
this._controller?.abort();
this._controller = null;
this._instance?.destroy();
this._instance = null;
this._ready = null;
this.removeAttribute("data-ready");
}
};
// src/components/z-view.js
var ZView = class extends ZSurface {
get defaultSize() {
return "xxl";
}
get surfaceType() {
return "view";
}
};
// src/components/z-spot.js
var ZSpot = class extends ZSurface {
get defaultSize() {
return "m";
}
get surfaceType() {
return "spot";
}
};
// src/components/control-utils.js
var HTMLElementBase3 = globalThis.HTMLElement ?? class {
};
var FULL_RING_RANGE = "359.99deg";
function isViewDefinition(element) {
const view = element.closest("z-view");
return view?.hasAttribute("name") && !view.hasAttribute("data-view-name") && view.parentElement?.localName === "z-canvas";
}
function numberAttribute2(element, name, fallback) {
const raw = element.getAttribute(name);
const value = raw === null || raw.trim() === "" ? NaN : Number(raw);
return Number.isFinite(value) ? value : fallback;
}
function reflectNumber(element, name, value) {
if (value === null || value === void 0) element.removeAttribute(name);
else if (Number.isFinite(Number(value))) element.setAttribute(name, String(value));
}
function emit(element, type, detail, options = {}) {
return element.dispatchEvent(new element.ownerDocument.defaultView.CustomEvent(
type,
{ bubbles: true, composed: true, detail, ...options }
));
}
function upgradeProperties(element, names) {
for (const name of names) {
if (!Object.prototype.hasOwnProperty.call(element, name)) continue;
const value = element[name];
delete element[name];
element[name] = value;
}
}
function createRing(element, withHandle = false) {
const document2 = element.ownerDocument;
element.classList.add("gravity-spot");
const orbit = Array.from(element.children).find((node) => node.classList.contains("z-control-orbit")) ?? document2.createElement("div");
orbit.className = "orbit-12 z-control-orbit";
orbit.setAttribute("aria-hidden", "true");
const progress = orbit.querySelector("o-progress") ?? document2.createElement("o-progress");
progress.className = "z-control-progress";
progress.setAttribute("variant", "stroke");
progress.setAttribute("max", "100");
orbit.append(progress);
let handle;
if (withHandle) {
handle = orbit.querySelector(".z-control-handle") ?? document2.createElement("span");
handle.className = "satellite z-control-handle";
handle.style.setProperty("--o-from", "0deg");
handle.style.setProperty("--o-angle", "0deg");
orbit.append(handle);
}
element.append(orbit);
return { orbit, progress, handle };
}
var RadialControl = class extends HTMLElementBase3 {
get step() {
const value = numberAttribute2(this, "step", 1);
return value > 0 ? value : 1;
}
set step(value) {
reflectNumber(this, "step", value);
}
get unit() {
return this.getAttribute("unit") ?? "";
}
set unit(value) {
this.setAttribute("unit", value ?? "");
}
get disabled() {
return this.hasAttribute("disabled");
}
set disabled(value) {
this.toggleAttribute("disabled", Boolean(value));
}
get value() {
return this.normalize(numberAttribute2(this, this.valueAttribute, this.min));
}
set value(value) {
reflectNumber(this, this.valueAttribute, this.normalize(Number(value)));
}
normalize(value) {
if (!Number.isFinite(value)) return this.min;
const bounded = Math.min(this.max, Math.max(this.min, value));
const stepped = this.min + Math.round((bounded - this.min) / this.step) * this.step;
return Math.min(this.max, Math.max(this.min, Number(stepped.toFixed(10))));
}
upgradeProperties(names) {
upgradeProperties(this, names);
}
connectedCallback() {
if (isViewDefinition(this)) return;
if (this._listeners) return;
this._ring ??= createRing(this, true);
this._ring.progress.toggleAttribute("interactive", this.controlKind === "scroll");
if (this.controlKind === "knob" && !this._valueLabel) {
this._valueLabel = this.querySelector(":scope > .z-knob-value") ?? this.ownerDocument.createElement("span");
this._valueLabel.className = "z-knob-value";
this._valueLabel.setAttribute("aria-hidden", "true");
this._numberLabel = this._valueLabel.querySelector(".z-knob-number") ?? this.ownerDocument.createElement("span");
this._numberLabel.className = "z-knob-number";
this._unitLabel = this._valueLabel.querySelector(".z-knob-unit") ?? this.ownerDocument.createElement("span");
this._unitLabel.className = "z-knob-unit";
this._valueLabel.append(this._numberLabel, this._unitLabel);
this.append(this._valueLabel);
}
this.classList.add(`z-${this.controlKind}`);
this.setAttribute("role", "slider");
if (!this.hasAttribute("aria-label") && !this.hasAttribute("aria-labelledby")) this.setAttribute("aria-label", this.defaultLabel);
this._listeners = new this.ownerDocument.defaultView.AbortController();
const options = { signal: this._listeners.signal };
this.addEventListener("keydown", (event) => this._onKey(event), options);
this.addEventListener("pointerdown", (event) => this._onPointerDown(event), options);
this.ownerDocument.addEventListener("pointermove", (event) => this._onPointerMove(event), options);
this.ownerDocument.addEventListener("pointerup", (event) => this._onPointerEnd(event), options);
this.ownerDocument.addEventListener("pointercancel", (event) => this._onPointerEnd(event), options);
this.addEventListener("click", (event) => event.stopPropagation(), options);
this._render();
}
disconnectedCallback() {
this._releasePointer();
this._listeners?.abort();
this._listeners = null;
}
attributeChangedCallback() {
this._render();
}
_render() {
if (!this._ring) return;
const span = this.max - this.min;
const progress = span ? (this.value - this.min) / span * 100 : 0;
const angle = this.startAngle + progress / 100 * this.angleRange;
this.style.setProperty(`--z-${this.controlKind}-angle`, `${angle}deg`);
this._ring.progress.setAttribute("value", String(this.controlKind === "scroll" ? 100 : progress));
this._ring.progress.style.setProperty("--o-range", this.angleRange === 360 ? FULL_RING_RANGE : `${this.angleRange}deg`);
this._ring.progress.style.setProperty("--o-from", `${this.startAngle + 90}deg`);
this._ring.handle.style.setProperty("--o-offset", `${angle}deg`);
if (this._valueLabel) {
this._numberLabel.textContent = String(this.value);
this._unitLabel.textContent = this.unit;
this._unitLabel.hidden = !this.unit;
}
this.setAttribute("aria-valuemin", String(this.min));
this.setAttribute("aria-valuemax", String(this.max));
this.setAttribute("aria-valuenow", String(this.value));
this.setAttribute("aria-valuetext", `${this.value}${this.unit ? ` ${this.unit}` : ""}`);
this.setAttribute("aria-disabled", String(this.disabled));
if (this.disabled) {
if (this.tabIndex >= 0) this._enabledTabIndex = this.tabIndex;
this.tabIndex = -1;
this._releasePointer();
} else if (!this.hasAttribute("tabindex") || this.tabIndex === -1) this.tabIndex = this._enabledTabIndex ?? 0;
}
valueFromPointer(event) {
const bounds = this.getBoundingClientRect();
const angle = (Math.atan2(
event.clientY - bounds.top - bounds.height / 2,
event.clientX - bounds.left - bounds.width / 2
) * 180 / Math.PI + 360) % 360;
return this.min + angle / 360 * (this.max - this.min);
}
_input(value) {
const previous = this.value;
this.value = value;
if (this.value === previous) return false;
emit(this, "input", this.eventDetail);
return true;
}
_onKey(event) {
if (this.disabled || event.altKey || event.ctrlKey || event.metaKey) return;
const values = {
ArrowUp: this.value + this.step,
ArrowRight: this.value + this.step,
ArrowDown: this.value - this.step,
ArrowLeft: this.value - this.step,
PageUp: this.value + this.step * 10,
PageDown: this.value - this.step * 10,
Home: this.min,
End: this.max
};
if (!(event.key in values)) return;
event.preventDefault();
event.stopPropagation();
if (this._input(values[event.key])) emit(this, "change", this.eventDetail);
}
_onPointerDown(event) {
if (this.disabled || event.button !== 0 || this._pointerId !== void 0) return;
event.preventDefault();
event.stopPropagation();
this.focus({ preventScroll: true });
this._pointerId = event.pointerId;
this._pointerStart = this.value;
try {
this.setPointerCapture(event.pointerId);
} catch {
}
this._input(this.valueFromPointer(event));
}
_onPointerMove(event) {
if (this._pointerId === void 0 || event.pointerId !== this._pointerId) return;
event.preventDefault();
this._input(this.valueFromPointer(event));
}
_onPointerEnd(event) {
if (this._pointerId === void 0 || event.pointerId !== this._pointerId) return;
const changed = this.value !== this._pointerStart;
this._releasePointer();
if (changed) emit(this, "change", this.eventDetail);
}
_releasePointer() {
if (this._pointerId === void 0) return;
try {
this.releasePointerCapture(this._pointerId);
} catch {
}
this._pointerId = void 0;
}
};
// src/components/z-list.js
var ZList = class extends HTMLElementBase3 {
static observedAttributes = ["per-page", "page", "size", "square"];
get perPage() {
return Math.max(1, Math.floor(numberAttribute2(this, "per-page", 5)));
}
set perPage(value) {
reflectNumber(this, "per-page", value);
}
get page() {
return Math.max(1, Math.min(this.pageCount || 1, Math.floor(numberAttribute2(this, "page", 1))));
}
set page(value) {
reflectNumber(this, "page", value);
}
get pageCount() {
return Math.ceil(this._spots().length / this.perPage);
}
get size() {
return normaliseSize(this.getAttribute("size"), "xxl");
}
set size(value) {
this.setAttribute("size", value);
}
get square() {
return this.hasAttribute("square");
}
set square(value) {
this.toggleAttribute("square", Boolean(value));
}
get items() {
return this._items?.slice() ?? [];
}
set items(value) {
if (!Array.isArray(value)) throw new TypeError("z-list.items must be an array.");
this._items = value.slice();
this._itemsDirty = true;
if (this.isConnected && this._pager) this._render();
}
get renderItem() {
return this._renderItem;
}
set renderItem(value) {
if (value != null && typeof value !== "function") throw new TypeError("z-list.renderItem must be a function.");
this._renderItem = value;
this._itemsDirty = true;
if (this.isConnected && this._pager) this._render();
}
connectedCallback() {
if (isViewDefinition(this)) return;
upgradeProperties(this, ["items", "renderItem", "page", "perPage", "size", "square"]);
this.classList.add("z-list", "gravity-spot");
if (!this._orbit) {
this._orbit = this.querySelector(":scope > .z-list-orbit") ?? this.ownerDocument.createElement("div");
this._orbit.className = "orbit-12 z-list-orbit";
this._orbit.style.setProperty("--o-range", "0deg");
this._pager = this.querySelector(":scope > .z-list-pagination") ?? this.ownerDocument.createElement("nav");
this._pager.className = "z-list-pagination orbit-12";
this._pager.style.setProperty("--o-range", "0deg");
this._pager.setAttribute("aria-label", "List pages");
this.append(this._orbit, this._pager);
}
if (!this._listeners) {
this._listeners = new this.ownerDocument.defaultView.AbortController();
this._pager.addEventListener("click", (event) => {
const button = event.target.closest("button[data-page]");
if (!button || button.disabled || !this._pager.contains(button)) return;
event.stopPropagation();
this.page = Number(button.dataset.page);
}, { signal: this._listeners.signal });
}
this._observer ??= new this.ownerDocument.defaultView.MutationObserver(() => this._render());
this._render();
}
disconnectedCallback() {
this._observer?.disconnect();
this._listeners?.abort();
this._listeners = null;
}
attributeChangedCallback() {
if (this.isConnected && this._pager) this._render();
}
next() {
this.page += 1;
return this.page;
}
previous() {
this.page -= 1;
return this.page;
}
_spots() {
const direct = Array.from(this.children).filter((child) => child.localName === "z-spot");
return this._orbit ? [...this._orbit.children, ...direct].filter((child) => child.localName === "z-spot") : direct;
}
_renderItems() {
if (!this._itemsDirty || !this._items) return;
const nodes = this._items.map((item, index) => {
const rendered = this._renderItem ? this._renderItem(item, index) : typeof item === "object" && item !== null ? item.label ?? item.name ?? String(item) : String(item);
if (rendered?.nodeType === 1 && rendered.localName === "z-spot") return rendered;
const spot = this.ownerDocument.createElement("z-spot");
if (rendered?.nodeType) spot.append(rendered);
else spot.textContent = rendered == null ? "" : String(rendered);
return spot;
});
for (const node of this._generated ?? []) if (!nodes.includes(node)) node.remove();
this._generated = nodes;
this._orbit.append(...nodes);
this._itemsDirty = false;
}
_renderPager(count, page) {
const first = count > 5 ? Math.max(1, Math.min(page - 2, count - 4)) : 1;
const pages = Array.from({ length: Math.min(5, count) }, (_, index) => ({
kind: "page",
page: first + index,
text: String(first + index),
label: `Page ${first + index}`
}));
if (count > 5) {
pages.unshift({ kind: "previous", page: Math.max(1, page - 1), text: "\u2039", label: "Previous page", disabled: page === 1 });
pages.push({ kind: "next", page: Math.min(count, page + 1), text: "\u203A", label: "Next page", disabled: page === count });
}
const signature = pages.map((item) => item.kind === "page" ? item.page : item.kind).join(",");
let restoreFocus;
if (signature !== this._pagerSignature) {
const active = this.ownerDocument.activeElement;
if (active?.parentElement === this._pager) restoreFocus = { kind: active.dataset.kind, page: active.dataset.page };
const fragment = this.ownerDocument.createDocumentFragment();
for (const item of pages) {
const button = this.ownerDocument.createElement("button");
button.className = `z-list-page satellite${item.kind === "page" ? "" : ` z-list-${item.kind}`}`;
button.type = "button";
fragment.append(button);
}
this._pager.replaceChildren(fragment);
this._pagerSignature = signature;
}
this._pager.hidden = count < 2;
[...this._pager.children].forEach((button, index) => {
const item = pages[index];
button.dataset.page = String(item.page);
button.dataset.kind = item.kind;
button.textContent = item.text;
button.setAttribute("aria-label", item.label);
button.disabled = Boolean(item.disabled);
button.style.setProperty("--o-offset", `${90 + (pages.length - 1) * 11 - index * 22}deg`);
button.style.setProperty("--o-from", "0deg");
button.style.setProperty("--o-angle", "0deg");
if (item.kind === "page" && item.page === page) button.setAttribute("aria-current", "page");
else button.removeAttribute("aria-current");
});
if (restoreFocus && count > 1) {
const buttons = [...this._pager.children];
const target = buttons.find((button) => button.dataset.kind === restoreFocus.kind && (restoreFocus.kind !== "page" || button.dataset.page === restoreFocus.page)) ?? buttons.find((button) => button.getAttribute("aria-current") === "page");
target?.focus({ preventScroll: true });
}
}
_render() {
if (!this._pager || this._rendering) return;
this._rendering = true;
this._observer?.disconnect();
try {
this._renderItems();
for (const child of Array.from(this.children)) if (child.localName === "z-spot") this._orbit.append(child);
const spots = this._spots();
const page = this.page;
const count = this.pageCount;
const first = (page - 1) * this.perPage;
const visible = spots.slice(first, first + this.perPage);
this._originalDistances ??= /* @__PURE__ */ new WeakMap();
this._originalAlignments ??= /* @__PURE__ */ new WeakMap();
for (const spot of this._managedSpots ?? []) {
if (spots.includes(spot)) continue;
const alignment = this._originalAlignments.get(spot);
if (alignment) spot.style.setProperty("--o-aligment", alignment);
else spot.style.removeProperty("--o-aligment");
spot.classList.remove("at-center");
spot.hidden = false;
}
this._managedSpots = spots;
spots.forEach((spot) => {
spot.hidden = !visible.includes(spot);
if (!this._originalDistances.has(spot)) {
const original = spot.hasAttribute("data-z-list-distance") ? spot.getAttribute("data-z-list-distance") || null : spot.getAttribute("distance");
this._originalDistances.set(spot, original);
spot.setAttribute("data-z-list-distance", original ?? "");
}
if (!this._originalAlignments.has(spot)) this._originalAlignments.set(spot, spot.style.getPropertyValue("--o-aligment"));
});
visible.forEach((spot, index) => {
spot.setAttribute("angle", String(360 / visible.length * index - 90));
spot.classList.toggle("at-center", visible.length === 1);
const distance = visible.length === 1 ? "0" : this._originalDistances.get(spot) ?? "100";
spot.setAttribute("distance", distance);
spot.style.setProperty("--o-aligment", `calc(var(--o-radius) * ${1 - Math.max(0, numberAttribute2(spot, "distance", 100)) / 100})`);
});
this.style.setProperty("--z-list-size", `var(--z-size-${this.size}, var(--z-size-xxl))`);
this.classList.toggle("is-square", this.hasAttribute("square"));
this._renderPager(count, page);
const previousPage = this._lastPage;
this._lastPage = page;
if (this.hasAttribute("page") && this.getAttribute("page") !== String(page)) this.setAttribute("page", String(page));
if (previousPage !== void 0 && previousPage !== page) emit(this, "pagechange", { page, pageCount: count, previousPage });
} finally {
this._rendering = false;
if (this.isConnected) {
this._observer?.observe(this, { childList: true });
this._observer?.observe(this._orbit, { childList: true });
}
}
}
};
// src/components/z-dialog.js
var ZDialog = class extends HTMLElementBase3 {
static observedAttributes = ["open", "visible", "duration", "self-close", "size", "square", "circle", "image-path", "aria-label", "aria-labelledby", "aria-describedby"];
get open() {
return this.hasAttribute("open") || this.hasAttribute("visible");
}
set open(value) {
if (value) this.show();
else this.close();
}
get visible() {
return this.open;
}
set visible(value) {
this.open = value;
}
get duration() {
return Math.max(0, numberAttribute2(this, "duration", this.selfClose ? 1e4 : 0));
}
set duration(value) {
reflectNumber(this, "duration", value);
}
get selfClose() {
return this.hasAttribute("self-close");
}
set selfClose(value) {
this.toggleAttribute("self-close", Boolean(value));
}
get size() {
return normaliseSize(this.getAttribute("size"), "xxl");
}
set size(value) {
this.setAttribute("size", value);
}
get square() {
return this.hasAttribute("square");
}
set square(value) {
this.toggleAttribute("square", Boolean(value));
}
get circle() {
return this.hasAttribute("circle");
}
set circle(value) {
this.toggleAttribute("circle", Boolean(value));
}
get imagePath() {
return this.getAttribute("image-path") ?? "";
}
set imagePath(value) {
if (value) this.setAttribute("image-path", value);
else this.removeAttribute("image-path");
}
get returnValue() {
return this._panel?.returnValue ?? "";
}
connectedCallback() {
if (isViewDefinition(this)) return;
upgradeProperties(this, ["open", "visible", "duration", "selfClose", "size", "imagePath", "square", "circle"]);
this.classList.add("z-dialog");
if (!this._panel) this._build();
if (!this._listeners) {
this._listeners = new this.ownerDocument.defaultView.AbortController();
const options = { signal: this._listeners.signal };
this._closeButton.addEventListener("click", () => this.close("", "button"), options);
this._panel.addEventListener("cancel", (event) => {
event.preventDefault();
if (emit(this, "cancel", {}, { cancelable: true })) this.close("", "escape");
}, options);
this._panel.addEventListener("close", () => {
if (this._visible && !this._panel.open) this.close(this._panel.returnValue, "form");
}, options);
this._panel.addEventListener("click", (event) => {
event.stopPropagation();
if (event.target !== this._panel) return;
const rect = this._panel.getBoundingClientRect();
if (event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom) this.close("", "backdrop");
}, options);
this._panel.addEventListener("keydown", (event) => this._fallbackKeys(event), options);
this._content.addEventListener("scroll", () => this._measureScroll(), { ...options, passive: true });
this._scroll.addEventListener("input", (event) => {
event.stopPropagation();
const span = this._content.scrollHeight - this._content.clientHeight;
if (span > 0) this._content.scrollTop = (event.detail.scrollVal + 45) / 90 * span;
}, options);
}
this._observer ??= new this.ownerDocument.defaultView.MutationObserver(() => this._adoptContent());
const Resize = this.ownerDocument.defaultView.ResizeObserver;
if (Resize) this._resize ??= new Resize(() => this._measureScroll());
this._adoptContent();
this._observer.observe(this, { childList: true, subtree: true, characterData: true, attributes: true, attributeFilter: ["slot"] });
this._render();
}
disconnectedCallback() {
this._stopTimer();
this._observer?.disconnect();
this._resize?.disconnect();
this._listeners?.abort();
this._listeners = null;
if (this._panel?.open) this._panel.close?.();
this._panel?.removeAttribute("open");
this._visible = false;
this._restoreFocus();
}
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue === newValue || !this._panel || this._syncing) return;
this._render();
if (this._visible && (name === "duration" || name === "self-close")) this._startTimer();
}
show() {
this.hidden = false;
this.setAttribute("open", "");
if (this.isConnected) this._render();
return this;
}
close(returnValue = "", reason = "api") {
const wasOpen = this._visible;
this._syncing = true;
this.removeAttribute("open");
this.removeAttribute("visible");
this._syncing = false;
this._stopTimer();
this._visible = false;
if (this._panel) {
this._panel.returnValue = String(returnValue);
if (this._panel.open && typeof this._panel.close === "function") this._panel.close(String(returnValue));
this._panel.removeAttribute("open");
}
this._restoreFocus();
if (wasOpen) emit(this, "close", { returnValue: String(returnValue), reason });
return this;
}
_build() {
const document2 = this.ownerDocument;
this._panel = this.querySelector(":scope > .z-dialog-panel") ?? document2.createElement("dialog");
this._panel.className = "z-dialog-panel";
this._image = this._panel.querySelector(":scope > img.z-dialog-image");
this._closeButton = this._panel.querySelector(":scope > .z-dialog-close") ?? document2.createElement("button");
this._closeButton.type = "button";
this._closeButton.className = "z-dialog-close";
this._closeButton.setAttribute("aria-label", "Close dialog");
this._closeButton.textContent = "\xD7";
this._content = this._panel.querySelector(":scope > .z-dialog-content") ?? document2.createElement("div");
this._content.className = "z-dialog-content";
this._progress = this._panel.querySelector(":scope > .z-dialog-progress") ?? document2.createElement("z-slider");
this._progress.className = "z-dialog-progress";
this._progress.setAttribute("aria-label", "Time until dialog closes");
this._progress.setAttribute("aria-hidden", "true");
this._scroll = this._panel.querySelector(":scope > .z-dialog-scroll") ?? document2.createElement("z-scroll");
this._scroll.className = "z-dialog-scroll";
this._scroll.setAttribute("aria-label", "Scroll dialog content");
this._scroll.hidden = true;
this._imageSlot = this._panel.querySelector(":scope > .z-dialog-image-slot") ?? document2.createElement("div");
this._imageSlot.className = "z-dialog-image-slot";
this._media = this._panel.querySelector(":scope > .z-dialog-media") ?? document2.createElement("div");
this._media.className = "z-dialog-media";
this._extensions = this._panel.querySelector(":scope > .z-dialog-extensions") ?? document2.createElement("div");
this._extensions.className = "z-dialog-extensions";
this._panel.append(this._imageSlot, this._content, this._media, this._extensions, this._progress, this._scroll, this._closeButton);
this.append(this._panel);
}
_adoptContent() {
const nodes = [...this.childNodes, ...this._content.childNodes, ...this._imageSlot.childNodes, ...this._media.childNodes, ...this._extensions.childNodes];
for (const child of nodes) {
if (child === this._panel) continue;
const slot = child.nodeType === 1 ? child.getAttribute("slot") : null;
const target = slot === "image" ? this._imageSlot : slot === "media" ? this._media : slot === "extension" ? this._extensions : this._content;
if (child.parentNode !== target) target.append(child);
}
this._imageSlot.hidden = !this._imageSlot.childNodes.length || Boolean(this.imagePath);
this._media.hidden = !this._media.childNodes.length;
this._extensions.hidden = !this._extensions.childNodes.length;
this._resize?.disconnect();
if (this.isConnected) {
this._resize?.observe(this._content);
for (const child of this._content.children) this._resize?.observe(child);
}
this._measureScroll();
}
_render() {
if (!this._panel) return;
this.style.setProperty("--z-dialog-size", `var(--z-size-${this.size}, var(--z-size-xxl))`);
this._panel.classList.toggle("is-square", this.hasAttribute("square"));
this._panel.classList.toggle("is-circle", !this.hasAttribute("square"));
for (const name of ["aria-label", "aria-labelledby", "aria-describedby"]) {
if (this.hasAttribute(name)) this._panel.setAttribute(name, this.getAttribute(name));
else this._panel.removeAttribute(name);
}
if (!this._panel.hasAttribute("aria-label") && !this._panel.hasAttribute("aria-labelledby")) this._panel.setAttribute("aria-label", "Dialog");
if (this.imagePath) {
if (!this._image) {
this._image = this.ownerDocument.createElement("img");
this._image.className = "z-dialog-image";
this._image.alt = "";
this._panel.prepend(this._image);
}
this._image.src = this.imagePath;
} else if (this._image) {
this._image.remove();
this._image = null;
}
this._imageSlot.hidden = !this._imageSlot.childNodes.length || Boolean(this.imagePath);
this._progress.hidden = this.duration === 0;
if (!this.isConnected) return;
if (this.open && !this._visible) {
this._returnFocus = this.ownerDocument.activeElement;
this.hidden = false;
this._visible = true;
this._panel.returnValue = "";
if (typeof this._panel.showModal === "function") {
this._panel.removeAttribute("open");
this._panel.showModal();
} else {
this._panel.setAttribute("open", "");
this._panel.setAttribute("role", "dialog");
this._panel.setAttribute("aria-modal", "true");
this._closeButton.focus();
}
this._startTimer();
this._measureScroll();
emit(this, "open", {});
} else if (!this.open && this._visible) this.close("", "attribute");
}
_measureScroll() {
if (!this._scroll) return;
const span = this._content.scrollHeight - this._content.clientHeight;
this._scroll.hidden = !this._visible || span < 2 || this.hasAttribute("square");
this._scroll.scrollVal = span > 0 ? -45 + this._content.scrollTop / span * 90 : -45;
}
_startTimer() {
this._stopTimer();
if (!this.duration || !this._visible) return;
const window2 = this.ownerDocument.defaultView;
const duration = this.duration;
const start = window2.performance.now();
this._progress.progress = 0;
const tick = () => {
const elapsed = window2.performance.now() - start;
this._progress.progress = Math.min(100, elapsed / duration * 100);
if (this._visible && elapsed < duration) this._timerFrame = window2.requestAnimationFrame(tick);
};
this._timerFrame = window2.requestAnimationFrame(tick);
this._timer = window2.setTimeout(() => {
this._progress.progress = 100;
this.close("", "timeout");
emit(this, "done", { reason: "timeout" });
}, duration);
}
_stopTimer() {
const window2 = this.ownerDocument.defaultView;
window2.clearTimeout(this._timer);
window2.cancelAnimationFrame(this._timerFrame);
this._timer = this._timerFrame = null;
}
_restoreFocus() {
if (this._returnFocus?.isConnected && typeof this._returnFocus.focus === "function") this._returnFocus.focus({ preventScroll: true });
this._returnFocus = null;
}
_fallbackKeys(event) {
if (typeof this._panel.showModal === "function") return;
if (event.key === "Escape") {
event.preventDefault();
if (emit(this, "cancel", {}, { cancelable: true })) this.close("", "escape");
}
if (event.key !== "Tab") return;
const focusable = [...this._panel.querySelectorAll("a[href],button,input,select,textarea,[tabindex]")].filter((element) => !element.disabled && element.tabIndex >= 0 && !element.hidden && element.getClientRects().length);
const first = focusable[0] ?? this._closeButton;
const last = focusable.at(-1) ?? this._closeButton;
if (event.shiftKey && this.ownerDocument.activeElement === first) {
event.preventDefault();
last.focus();
} else if (!event.shiftKey && this.ownerDocument.activeElement === last) {
event.preventDefault();
first.focus();
}
}
};
// src/components/z-knob.js
var ZKnob = class extends RadialControl {
static observedAttributes = ["qty", "min", "max", "step", "unit", "disabled"];
get controlKind() {
return "knob";
}
get min() {
return numberAttribute2(this, "min", 0);
}
set min(value) {
reflectNumber(this, "min", value);
}
get max() {
return Math.max(this.min, numberAttribute2(this, "max", 100));
}
set max(value) {
reflectNumber(this, "max", value);
}
get qty() {
return this.value;
}
set qty(value) {
this.value = value;
}
get valueAttribute() {
return "qty";
}
get startAngle() {
return 0;
}
get angleRange() {
return 360;
}
get defaultLabel() {
return "Value";
}
get eventDetail() {
return { value: this.value, qty: this.value };
}
connectedCallback() {
this.upgradeProperties(["qty", "min", "max", "step", "unit", "value", "disabled"]);
super.connectedCallback();
}
};
// src/components/z-slider.js
var ZSlider = class extends HTMLElementBase3 {
static observedAttributes = ["progress", "unit"];
get progress() {
return Math.max(0, Math.min(100, numberAttribute2(this, "progress", 0)));
}
set progress(value) {
reflectNumber(this, "progress", value);
}
get value() {
return this.progress;
}
set value(value) {
this.progress = value;
}
get unit() {
return this.getAttribute("unit") ?? "%";
}
set unit(value) {
this.setAttribute("unit", value ?? "");
}
connectedCallback() {
if (isViewDefinition(this)) return;
upgradeProperties(this, ["progress", "value", "unit"]);
this._ring ??= createRing(this);
this.classList.add("z-slider");
this.setAttribute("role", "progressbar");
this.setAttribute("aria-valuemin", "0");
this.setAttribute("aria-valuemax", "100");
if (!this.hasAttribute("aria-label") && !this.hasAttribute("aria-labelledby")) this.setAttribute("aria-label", "Progress");
this._render();
}
attributeChangedCallback() {
this._render();
}
_render() {
if (!this._ring) return;
this._ring.progress.setAttribute("value", String(this.progress));
this._ring.progress.style.setProperty("--o-range", FULL_RING_RANGE);
this._ring.progress.style.setProperty("--o-from", "0deg");
this.setAttribute("aria-valuenow", String(this.progress));
this.setAttribute("aria-valuetext", `${this.progress}${this.unit}`);
}
};
// src/components/z-scroll.js
var ZScroll = class extends RadialControl {
static observedAttributes = ["scroll-val", "step", "unit", "disabled"];
get controlKind() {
return "scroll";
}
get min() {
return -45;
}
get max() {
return 45;
}
get scrollVal() {
return this.value;
}
set scrollVal(value) {
this.value = value;
}
get valueAttribute() {
return "scroll-val";
}
get startAngle() {
return -45;
}
get angleRange() {
return 90;
}
get defaultLabel() {
return "Scroll position";
}
get eventDetail() {
return { value: this.value, scrollVal: this.value };
}
connectedCallback() {
this.upgradeProperties(["scrollVal", "step", "unit", "value", "disabled"]);
super.connectedCallback();
}
valueFromPointer(event) {
const bounds = this.getBoundingClientRect();
return this.normalize(Math.atan2(
event.clientY - bounds.top - bounds.height / 2,
event.clientX - bounds.left - bounds.width / 2
) * 180 / Math.PI);
}
};
// src/components/z-pagination.js
var ZPagination = class extends HTMLElementBase3 {
static observedAttributes = ["index", "active", "angle", "distance", "size", "disabled"];
get index() {
return Math.max(0, Math.floor(numberAttribute2(this, "index", 0)));
}
set index(value) {
reflectNumber(this, "index", value);
}
get active() {
return Math.max(0, Math.floor(numberAttribute2(this, "active", 0)));
}
set active(value) {
reflectNumber(this, "active", value);
}
get angle() {
return numberAttribute2(this, "angle", 0);
}
set angle(value) {
reflectNumber(this, "angle", value);
}
get distance() {
return Math.max(0, numberAttribute2(this, "distance", 100));
}
set distance(value) {
reflectNumber(this, "distance", value);
}
get size() {
return normaliseSize(this.getAttribute("size"), "xs");
}
set size(value) {
this.setAttribute("size", value);
}
get disabled() {
return this.hasAttribute("disabled");
}
set disabled(value) {
this.toggleAttribute("disabled", Boolean(value));
}
connectedCallback() {
if (isViewDefinition(this)) return;
upgradeProperties(this, ["index", "active", "angle", "distance", "size", "disabled"]);
if (!this._button) {
this._button = this.querySelector(":scope > .z-pagination-button") ?? this.ownerDocument.createElement("button");
this._button.type = "button";
this._button.className = "z-pagination-button";
this.append(this._button);
}
this.classList.add("z-pagination", "satellite");
if (!this._listeners) {
this._listeners = new this.ownerDocument.defaultView.AbortController();
this._button.addEventListener("click", (event) => {
event.stopPropagation();
if (!this.disabled) emit(this, "change", { index: this.index, page: this.index + 1 });
}, { signal: this._listeners.signal });
}
this._render();
}
disconnectedCallback() {
this._listeners?.abort();
this._listeners = null;
}
attributeChangedCallback() {
this._render();
}
_render() {
if (!this._button) return;
this._button.textContent = String(this.index + 1);
this._button.setAttribute("aria-label", `Page ${this.index + 1}`);
this._button.disabled = this.disabled;
this._button.toggleAttribute("data-active", this.index === this.active);
if (this.index === this.active) this._button.setAttribute("aria-current", "page");
else this._button.removeAttribute("aria-current");
this.classList.toggle("active", this.index === this.active);
this.classList.toggle("deactive", this.index !== this.active);
this.style.setProperty("--o-from", `${this.angle}deg`);
this.style.setProperty("--o-offset", "0deg");
this.style.setProperty("--o-angle", "0deg");
this.style.setProperty("--z-pagination-distance", String(this.distance / 100));
this.style.setProperty("--o-aligment", `calc(var(--o-radius) * ${1 - this.distance / 100})`);
this.style.setProperty("--z-pagination-size", `var(--z-size-${this.size}, var(--z-size-xs))`);
}
};
// src/components/index.js
function registerElements() {
if (typeof customElements === "undefined") return;
registerOrbit();
const elements = { "z-canvas": ZCanvas, "z-view": ZView, "z-spot": ZSpot, "z-list": ZList, "z-dialog": ZDialog, "z-knob": ZKnob, "z-slider": ZSlider, "z-scroll": ZScroll, "z-pagination": ZPagination };
for (const [name, ctor] of Object.entries(elements)) {
if (!customElements.get(name)) customElements.define(name, ctor);
}
}
// src/zircle.js
if (typeof window !== "undefined") registerElements();
export {
MODES,
Orbit,
THEMES,
ZCanvas,
ZDialog,
ZKnob,
ZList,
ZPagination,
ZScroll,
ZSlider,
ZSpot,
ZView,
gt as Zumly,
Ze as ZumlyRouter,
createZircle,
createZircle as default,
registerElements
};
//# sourceMappingURL=zircle.standalone.js.map