{"version":3,"file":"escapeStack.cjs","sources":["../../../../src/components/utils/escapeStack.ts"],"sourcesContent":["import { getDescendantOverlays } from './overlayStack';\n\n// Escape closes the TOPMOST overlay. \"Topmost\" follows the overlay tree, not the\n// push order: React runs child effects before parent effects, so a popup opened\n// inside a modal pushes its handler BEFORE the modal's — firing by push order\n// alone would close the modal instead of the inner popup. We instead fire the\n// most-recently-registered handler whose overlay has no open descendant overlay\n// that also has a handler.\ntype EscapeHandler = () => void;\ninterface EscapeEntry {\n  handler: EscapeHandler;\n  el: HTMLElement | null;\n}\n\nconst stack: EscapeEntry[] = [];\nlet listenerAttached = false;\n\nfunction handleKeyDown(event: KeyboardEvent) {\n  if (event.key !== 'Escape' || stack.length === 0) return;\n  event.preventDefault();\n\n  const activeEls = new Set(stack.map((s) => s.el).filter((el): el is HTMLElement => el != null));\n  const hasActiveDescendant = (el: HTMLElement | null) =>\n    el ? getDescendantOverlays(el).some((d) => activeEls.has(d)) : false;\n\n  let chosen: EscapeEntry | undefined;\n  for (let i = stack.length - 1; i >= 0; i--) {\n    if (!hasActiveDescendant(stack[i].el)) {\n      chosen = stack[i];\n      break;\n    }\n  }\n  (chosen ?? stack[stack.length - 1]).handler();\n}\n\nexport function pushEscapeHandler(handler: EscapeHandler, el: HTMLElement | null = null): () => void {\n  if (!listenerAttached && typeof document !== 'undefined') {\n    document.addEventListener('keydown', handleKeyDown);\n    listenerAttached = true;\n  }\n  stack.push({ handler, el });\n  return () => {\n    const idx = stack.findIndex((e) => e.handler === handler);\n    if (idx !== -1) stack.splice(idx, 1);\n  };\n}\n\n// test cleanup\nexport function resetEscapeStack() {\n  stack.length = 0;\n  if (listenerAttached && typeof document !== 'undefined') {\n    document.removeEventListener('keydown', handleKeyDown);\n    listenerAttached = false;\n  }\n}\n"],"names":["getDescendantOverlays"],"mappings":";;;;AAcA,MAAM,QAAuB,EAAC;AAC9B,IAAI,gBAAA,GAAmB,KAAA;AAEvB,SAAS,cAAc,KAAA,EAAsB;AAC3C,EAAA,IAAI,KAAA,CAAM,GAAA,KAAQ,QAAA,IAAY,KAAA,CAAM,WAAW,CAAA,EAAG;AAClD,EAAA,KAAA,CAAM,cAAA,EAAe;AAErB,EAAA,MAAM,SAAA,GAAY,IAAI,GAAA,CAAI,KAAA,CAAM,IAAI,CAAC,CAAA,KAAM,CAAA,CAAE,EAAE,EAAE,MAAA,CAAO,CAAC,EAAA,KAA0B,EAAA,IAAM,IAAI,CAAC,CAAA;AAC9F,EAAA,MAAM,mBAAA,GAAsB,CAAC,EAAA,KAC3B,EAAA,GAAKA,mCAAsB,EAAE,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,KAAM,SAAA,CAAU,GAAA,CAAI,CAAC,CAAC,CAAA,GAAI,KAAA;AAEjE,EAAA,IAAI,MAAA;AACJ,EAAA,KAAA,IAAS,IAAI,KAAA,CAAM,MAAA,GAAS,CAAA,EAAG,CAAA,IAAK,GAAG,CAAA,EAAA,EAAK;AAC1C,IAAA,IAAI,CAAC,mBAAA,CAAoB,KAAA,CAAM,CAAC,CAAA,CAAE,EAAE,CAAA,EAAG;AACrC,MAAA,MAAA,GAAS,MAAM,CAAC,CAAA;AAChB,MAAA;AAAA,IACF;AAAA,EACF;AACA,EAAA,CAAC,UAAU,KAAA,CAAM,KAAA,CAAM,MAAA,GAAS,CAAC,GAAG,OAAA,EAAQ;AAC9C;AAEO,SAAS,iBAAA,CAAkB,OAAA,EAAwB,EAAA,GAAyB,IAAA,EAAkB;AACnG,EAAA,IAAI,CAAC,gBAAA,IAAoB,OAAO,QAAA,KAAa,WAAA,EAAa;AACxD,IAAA,QAAA,CAAS,gBAAA,CAAiB,WAAW,aAAa,CAAA;AAClD,IAAA,gBAAA,GAAmB,IAAA;AAAA,EACrB;AACA,EAAA,KAAA,CAAM,IAAA,CAAK,EAAE,OAAA,EAAS,EAAA,EAAI,CAAA;AAC1B,EAAA,OAAO,MAAM;AACX,IAAA,MAAM,MAAM,KAAA,CAAM,SAAA,CAAU,CAAC,CAAA,KAAM,CAAA,CAAE,YAAY,OAAO,CAAA;AACxD,IAAA,IAAI,GAAA,KAAQ,EAAA,EAAI,KAAA,CAAM,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,EACrC,CAAA;AACF;;;;"}