package.dist.index.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of collapsible Show documentation
Show all versions of collapsible Show documentation
Core logic for the collapsible widget implemented as a state machine
The newest version!
'use strict';
var anatomy$1 = require('@zag-js/anatomy');
var domQuery = require('@zag-js/dom-query');
var core = require('@zag-js/core');
var utils = require('@zag-js/utils');
var types = require('@zag-js/types');
// src/collapsible.anatomy.ts
var anatomy = anatomy$1.createAnatomy("collapsible").parts("root", "trigger", "content");
var parts = anatomy.build();
var dom = domQuery.createScope({
getRootId: (ctx) => ctx.ids?.root ?? `collapsible:${ctx.id}`,
getContentId: (ctx) => ctx.ids?.content ?? `collapsible:${ctx.id}:content`,
getTriggerId: (ctx) => ctx.ids?.trigger ?? `collapsible:${ctx.id}:trigger`,
getRootEl: (ctx) => dom.getById(ctx, dom.getRootId(ctx)),
getContentEl: (ctx) => dom.getById(ctx, dom.getContentId(ctx)),
getTriggerEl: (ctx) => dom.getById(ctx, dom.getTriggerId(ctx))
});
// src/collapsible.connect.ts
function connect(state, send, normalize) {
const visible = state.matches("open", "closing");
const open = state.matches("open");
const height = state.context.height;
const width = state.context.width;
const disabled = !!state.context.disabled;
const skip = !state.context.initial && open;
return {
disabled,
visible,
open,
setOpen(nextOpen) {
if (nextOpen === open) return;
send(nextOpen ? "OPEN" : "CLOSE");
},
getRootProps() {
return normalize.element({
...parts.root.attrs,
"data-state": open ? "open" : "closed",
dir: state.context.dir,
id: dom.getRootId(state.context)
});
},
getContentProps() {
return normalize.element({
...parts.content.attrs,
"data-state": skip ? void 0 : open ? "open" : "closed",
id: dom.getContentId(state.context),
"data-disabled": domQuery.dataAttr(disabled),
hidden: !visible,
style: {
"--height": height != null ? `${height}px` : void 0,
"--width": width != null ? `${width}px` : void 0
}
});
},
getTriggerProps() {
return normalize.element({
...parts.trigger.attrs,
id: dom.getTriggerId(state.context),
dir: state.context.dir,
type: "button",
"data-state": open ? "open" : "closed",
"data-disabled": domQuery.dataAttr(disabled),
"aria-controls": dom.getContentId(state.context),
"aria-expanded": visible || false,
onClick(event) {
if (event.defaultPrevented) return;
if (disabled) return;
send({ type: open ? "CLOSE" : "OPEN", src: "trigger.click" });
}
});
}
};
}
function machine(userContext) {
const ctx = utils.compact(userContext);
return core.createMachine(
{
id: "collapsible",
initial: ctx.open ? "open" : "closed",
context: {
...ctx,
height: 0,
width: 0,
initial: false,
stylesRef: null,
unmountAnimationName: null
},
watch: {
open: ["setInitial", "computeSize", "toggleVisibility"]
},
exit: ["clearInitial"],
states: {
closed: {
tags: ["closed"],
on: {
"CONTROLLED.OPEN": "open",
OPEN: [
{
guard: "isOpenControlled",
actions: ["invokeOnOpen"]
},
{
target: "open",
actions: ["setInitial", "computeSize", "invokeOnOpen"]
}
]
}
},
closing: {
tags: ["open"],
activities: ["trackAnimationEvents"],
on: {
"CONTROLLED.CLOSE": "closed",
"CONTROLLED.OPEN": "open",
OPEN: [
{
guard: "isOpenControlled",
actions: ["invokeOnOpen"]
},
{
target: "open",
actions: ["setInitial", "invokeOnOpen"]
}
],
CLOSE: [
{
guard: "isOpenControlled",
actions: ["invokeOnExitComplete"]
},
{
target: "closed",
actions: ["setInitial", "computeSize", "invokeOnExitComplete"]
}
],
"ANIMATION.END": {
target: "closed",
actions: ["invokeOnExitComplete"]
}
}
},
open: {
tags: ["open"],
on: {
"CONTROLLED.CLOSE": "closing",
CLOSE: [
{
guard: "isOpenControlled",
actions: ["invokeOnClose"]
},
{
target: "closing",
actions: ["setInitial", "computeSize", "invokeOnClose"]
}
]
}
}
}
},
{
guards: {
isOpenControlled: (ctx2) => !!ctx2["open.controlled"]
},
activities: {
trackAnimationEvents(ctx2, _evt, { send }) {
let cleanup;
const rafCleanup = domQuery.raf(() => {
const contentEl = dom.getContentEl(ctx2);
if (!contentEl) return;
const animationName = domQuery.getComputedStyle(contentEl).animationName;
const hasNoAnimation = !animationName || animationName === "none";
if (hasNoAnimation) {
send({ type: "ANIMATION.END" });
return;
}
const onEnd = (event) => {
const win = contentEl.ownerDocument.defaultView || window;
const animationName2 = win.getComputedStyle(contentEl).animationName;
const target = domQuery.getEventTarget(event);
if (target === contentEl && animationName2 === ctx2.unmountAnimationName) {
send({ type: "ANIMATION.END" });
}
};
contentEl.addEventListener("animationend", onEnd);
cleanup = () => {
contentEl.removeEventListener("animationend", onEnd);
};
});
return () => {
rafCleanup();
cleanup?.();
};
}
},
actions: {
setInitial(ctx2) {
ctx2.initial = true;
},
clearInitial(ctx2) {
ctx2.initial = false;
},
computeSize(ctx2, evt) {
ctx2._rafCleanup?.();
ctx2._rafCleanup = domQuery.raf(() => {
const contentEl = dom.getContentEl(ctx2);
if (!contentEl) return;
ctx2.stylesRef || (ctx2.stylesRef = core.ref({
animationName: contentEl.style.animationName,
animationDuration: contentEl.style.animationDuration
}));
if (evt.type === "CLOSE" || !ctx2.open) {
const win = contentEl.ownerDocument.defaultView || window;
ctx2.unmountAnimationName = win.getComputedStyle(contentEl).animationName;
}
const hidden = contentEl.hidden;
contentEl.style.animationName = "none";
contentEl.style.animationDuration = "0s";
contentEl.hidden = false;
const rect = contentEl.getBoundingClientRect();
ctx2.height = rect.height;
ctx2.width = rect.width;
if (ctx2.initial) {
contentEl.style.animationName = ctx2.stylesRef.animationName;
contentEl.style.animationDuration = ctx2.stylesRef.animationDuration;
}
contentEl.hidden = hidden;
});
},
invokeOnOpen: (ctx2) => {
ctx2.onOpenChange?.({ open: true });
},
invokeOnClose: (ctx2) => {
ctx2.onOpenChange?.({ open: false });
},
invokeOnExitComplete(ctx2) {
ctx2.onExitComplete?.();
},
toggleVisibility: (ctx2, _evt, { send }) => {
send({ type: ctx2.open ? "CONTROLLED.OPEN" : "CONTROLLED.CLOSE" });
}
}
}
);
}
var props = types.createProps()([
"dir",
"disabled",
"getRootNode",
"id",
"ids",
"onExitComplete",
"onOpenChange",
"open.controlled",
"open"
]);
var splitProps = utils.createSplitProps(props);
exports.anatomy = anatomy;
exports.connect = connect;
exports.machine = machine;
exports.props = props;
exports.splitProps = splitProps;
© 2015 - 2025 Weber Informatics LLC | Privacy Policy