@wildwinter/app-shell 0.46.1 → 0.46.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/anchored.css +4 -0
- package/dist/anchored.css.map +1 -1
- package/dist/{chunk-P4G4ERG6.js → chunk-PFERSFRB.js} +3 -2
- package/dist/chunk-PFERSFRB.js.map +1 -0
- package/dist/context-menu.css +6 -0
- package/dist/context-menu.css.map +1 -1
- package/dist/dialog.css +4 -0
- package/dist/dialog.css.map +1 -1
- package/dist/floating.cjs +1 -0
- package/dist/floating.cjs.map +1 -1
- package/dist/floating.js +1 -1
- package/dist/index.cjs +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/settings.css +4 -0
- package/dist/settings.css.map +1 -1
- package/dist/toast.css +4 -0
- package/dist/toast.css.map +1 -1
- package/dist/tokens.css +4 -0
- package/dist/tokens.css.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-P4G4ERG6.js.map +0 -1
package/dist/anchored.css
CHANGED
package/dist/anchored.css.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/anchored.css"],"sourcesContent":["/* The anchored panel (anchored.ts): a floating panel hung off the thing it is\n about. Sized and positioned in script (the placement has to measure); this is\n only what it looks like. Its body scrolls, because the placement caps the\n height rather than letting a long thread run off the screen. */\n.shell-anchored {\n position: fixed; z-index: 40;\n display: flex; flex-direction: column;\n border: 1px solid var(--line);\n border-radius: var(--radius-md);\n background: var(--surface); color: var(--ink);\n box-shadow: var(--shadow-2, var(--shadow-1));\n overflow: hidden;\n /* The family's panel motion. Placement is set in script as left/top, so a\n transform-based keyframe composes with it rather than fighting it. */\n animation: var(--anim-panel-in);\n}\n/* Exit: .closing swaps the enter keyframe for its reverse, and the fading panel\n stops taking clicks so a second press cannot reach a control on its way out.\n Zero-duration under prefers-reduced-motion, where teardown is immediate. */\n.shell-anchored.closing { animation: var(--anim-panel-out); pointer-events: none; }\n.shell-anchored-head {\n flex: none;\n display: flex; align-items: center; justify-content: space-between; gap: 8px;\n padding: 8px 10px;\n border-bottom: 1px solid var(--line-soft);\n}\n.shell-anchored-title {\n font: 600 13px/1.2 var(--font-ui);\n color: var(--muted);\n}\n.shell-anchored-close {\n display: inline-flex; align-items: center; justify-content: center;\n border: 0; background: none; color: var(--muted);\n line-height: 1; padding: 2px; cursor: pointer;\n}\n.shell-anchored-close:hover { color: var(--accent); }\n.shell-anchored-body {\n flex: 1; min-height: 0; overflow-y: auto;\n display: flex; flex-direction: column; gap: 8px;\n padding: 10px;\n}\n\n/* The gameId editor (id-editor.ts). Refusal is the whole point of the design, so\n it has to LOOK refused: the hint reddens and Set goes flat. */\n.shell-id-editor .shell-anchored-body { display: flex; flex-direction: column; gap: 8px; }\n.shell-id-input {\n font: 13px var(--font-mono, monospace);\n padding: 5px 8px; border: 1px solid var(--line); border-radius: var(--radius-sm);\n background: var(--card); color: var(--ink);\n}\n.shell-id-hint { font: 12px/1.4 var(--font-ui); color: var(--muted); }\n.shell-id-hint.bad { color: var(--danger, #b00); }\n.shell-id-actions { display: flex; align-items: center; justify-content: space-between; gap: 8px; }\n.shell-id-reset {\n font: 12px var(--font-ui); padding: 3px 10px; cursor: pointer;\n background: none; border: 1px dashed var(--line); border-radius: var(--radius-sm); color: var(--muted);\n}\n.shell-id-reset:hover { border-color: var(--accent); color: var(--accent); }\n.shell-id-set {\n font: 12.5px var(--font-ui); padding: 3px 12px; cursor: pointer;\n border: 1px solid var(--accent); border-radius: var(--radius-sm);\n background: var(--accent); color: var(--surface);\n}\n.shell-id-set:disabled {\n cursor: default; background: none; color: var(--muted); border-color: var(--line);\n}\n"],"mappings":";AAIA,CAAC;AACC,YAAU;AAAO,WAAS;AAC1B,WAAS;AAAM,kBAAgB;AAC/B,UAAQ,IAAI,MAAM,IAAI;AACtB,iBAAe,IAAI;AACnB,cAAY,IAAI;AAAY,SAAO,IAAI;AACvC,cAAY,IAAI,UAAU,EAAE,IAAI;AAChC,YAAU;AAGV,aAAW,IAAI;AACjB;AAIA,CAfC,cAec,CAAC;AAAU,aAAW,IAAI;AAAmB,kBAAgB;AAAM;AAClF,CAAC;AACC,QAAM;AACN,WAAS;AAAM,eAAa;AAAQ,mBAAiB;AAAe,OAAK;AACzE,WAAS,IAAI;AACb,iBAAe,IAAI,MAAM,IAAI;AAC/B;AACA,CAAC;AACC,QAAM,IAAI,IAAI,CAAC,IAAI,IAAI;AACvB,SAAO,IAAI;AACb;AACA,CAAC;AACC,WAAS;AAAa,eAAa;AAAQ,mBAAiB;AAC5D,UAAQ;AAAG,cAAY;AAAM,SAAO,IAAI;AACxC,eAAa;AAAG,WAAS;AAAK,UAAQ;AACxC;AACA,CALC,oBAKoB;AAAS,SAAO,IAAI;AAAW;AACpD,CAAC;AACC,QAAM;AAAG,cAAY;AAAG,cAAY;AACpC,WAAS;AAAM,kBAAgB;AAAQ,OAAK;AAC5C,WAAS;AACX;AAIA,CAAC,gBAAgB,CARhB;AAQuC,WAAS;AAAM,kBAAgB;AAAQ,OAAK;AAAK;AACzF,CAAC;AACC,QAAM,KAAK,IAAI,WAAW,EAAE;AAC5B,WAAS,IAAI;AAAK,UAAQ,IAAI,MAAM,IAAI;AAAS,iBAAe,IAAI;AACpE,cAAY,IAAI;AAAS,SAAO,IAAI;AACtC;AACA,CAAC;AAAgB,QAAM,IAAI,CAAC,IAAI,IAAI;AAAY,SAAO,IAAI;AAAU;AACrE,CADC,aACa,CAAC;AAAM,SAAO,IAAI,QAAQ,EAAE;AAAO;AACjD,CAAC;AAAmB,WAAS;AAAM,eAAa;AAAQ,mBAAiB;AAAe,OAAK;AAAK;AAClG,CAAC;AACC,QAAM,KAAK,IAAI;AAAY,WAAS,IAAI;AAAM,UAAQ;AACtD,cAAY;AAAM,UAAQ,IAAI,OAAO,IAAI;AAAS,iBAAe,IAAI;AAAc,SAAO,IAAI;AAChG;AACA,CAJC,cAIc;AAAS,gBAAc,IAAI;AAAW,SAAO,IAAI;AAAW;AAC3E,CAAC;AACC,QAAM,OAAO,IAAI;AAAY,WAAS,IAAI;AAAM,UAAQ;AACxD,UAAQ,IAAI,MAAM,IAAI;AAAW,iBAAe,IAAI;AACpD,cAAY,IAAI;AAAW,SAAO,IAAI;AACxC;AACA,CALC,YAKY;AACX,UAAQ;AAAS,cAAY;AAAM,SAAO,IAAI;AAAU,gBAAc,IAAI;AAC5E;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/anchored.css"],"sourcesContent":["/* The anchored panel (anchored.ts): a floating panel hung off the thing it is\n about. Sized and positioned in script (the placement has to measure); this is\n only what it looks like. Its body scrolls, because the placement caps the\n height rather than letting a long thread run off the screen. */\n.shell-anchored {\n position: fixed; z-index: 40;\n display: flex; flex-direction: column;\n border: 1px solid var(--line);\n border-radius: var(--radius-md);\n background: var(--surface); color: var(--ink);\n box-shadow: var(--shadow-2, var(--shadow-1));\n overflow: hidden;\n /* The family's panel motion. Placement is set in script as left/top, so a\n transform-based keyframe composes with it rather than fighting it. */\n animation: var(--anim-panel-in);\n}\n/* Exit: .closing swaps the enter keyframe for its reverse, and the fading panel\n stops taking clicks so a second press cannot reach a control on its way out.\n Zero-duration under prefers-reduced-motion, where teardown is immediate. */\n.shell-anchored.closing { animation: var(--anim-panel-out); pointer-events: none; }\n.shell-anchored-head {\n flex: none;\n display: flex; align-items: center; justify-content: space-between; gap: 8px;\n padding: 8px 10px;\n border-bottom: 1px solid var(--line-soft);\n}\n.shell-anchored-title {\n font: 600 13px/1.2 var(--font-ui);\n color: var(--muted);\n}\n.shell-anchored-close {\n display: inline-flex; align-items: center; justify-content: center;\n border: 0; background: none; color: var(--muted);\n line-height: 1; padding: 2px; cursor: pointer;\n}\n.shell-anchored-close:hover { color: var(--accent); }\n.shell-anchored-body {\n flex: 1; min-height: 0; overflow-y: auto;\n display: flex; flex-direction: column; gap: 8px;\n padding: 10px;\n}\n\n/* The gameId editor (id-editor.ts). Refusal is the whole point of the design, so\n it has to LOOK refused: the hint reddens and Set goes flat. */\n.shell-id-editor .shell-anchored-body { display: flex; flex-direction: column; gap: 8px; }\n.shell-id-input {\n font: 13px var(--font-mono, monospace);\n padding: 5px 8px; border: 1px solid var(--line); border-radius: var(--radius-sm);\n background: var(--card); color: var(--ink);\n}\n.shell-id-hint { font: 12px/1.4 var(--font-ui); color: var(--muted); }\n.shell-id-hint.bad { color: var(--danger, #b00); }\n.shell-id-actions { display: flex; align-items: center; justify-content: space-between; gap: 8px; }\n.shell-id-reset {\n font: 12px var(--font-ui); padding: 3px 10px; cursor: pointer;\n background: none; border: 1px dashed var(--line); border-radius: var(--radius-sm); color: var(--muted);\n}\n.shell-id-reset:hover { border-color: var(--accent); color: var(--accent); }\n.shell-id-set {\n font: 12.5px var(--font-ui); padding: 3px 12px; cursor: pointer;\n border: 1px solid var(--accent); border-radius: var(--radius-sm);\n background: var(--accent); color: var(--surface);\n}\n.shell-id-set:disabled {\n cursor: default; background: none; color: var(--muted); border-color: var(--line);\n}\n\n/* Never a window-drag region, even over one (dialog.css says why). */\n.shell-anchored, .shell-anchored * { -webkit-app-region: no-drag; }\n"],"mappings":";AAIA,CAAC;AACC,YAAU;AAAO,WAAS;AAC1B,WAAS;AAAM,kBAAgB;AAC/B,UAAQ,IAAI,MAAM,IAAI;AACtB,iBAAe,IAAI;AACnB,cAAY,IAAI;AAAY,SAAO,IAAI;AACvC,cAAY,IAAI,UAAU,EAAE,IAAI;AAChC,YAAU;AAGV,aAAW,IAAI;AACjB;AAIA,CAfC,cAec,CAAC;AAAU,aAAW,IAAI;AAAmB,kBAAgB;AAAM;AAClF,CAAC;AACC,QAAM;AACN,WAAS;AAAM,eAAa;AAAQ,mBAAiB;AAAe,OAAK;AACzE,WAAS,IAAI;AACb,iBAAe,IAAI,MAAM,IAAI;AAC/B;AACA,CAAC;AACC,QAAM,IAAI,IAAI,CAAC,IAAI,IAAI;AACvB,SAAO,IAAI;AACb;AACA,CAAC;AACC,WAAS;AAAa,eAAa;AAAQ,mBAAiB;AAC5D,UAAQ;AAAG,cAAY;AAAM,SAAO,IAAI;AACxC,eAAa;AAAG,WAAS;AAAK,UAAQ;AACxC;AACA,CALC,oBAKoB;AAAS,SAAO,IAAI;AAAW;AACpD,CAAC;AACC,QAAM;AAAG,cAAY;AAAG,cAAY;AACpC,WAAS;AAAM,kBAAgB;AAAQ,OAAK;AAC5C,WAAS;AACX;AAIA,CAAC,gBAAgB,CARhB;AAQuC,WAAS;AAAM,kBAAgB;AAAQ,OAAK;AAAK;AACzF,CAAC;AACC,QAAM,KAAK,IAAI,WAAW,EAAE;AAC5B,WAAS,IAAI;AAAK,UAAQ,IAAI,MAAM,IAAI;AAAS,iBAAe,IAAI;AACpE,cAAY,IAAI;AAAS,SAAO,IAAI;AACtC;AACA,CAAC;AAAgB,QAAM,IAAI,CAAC,IAAI,IAAI;AAAY,SAAO,IAAI;AAAU;AACrE,CADC,aACa,CAAC;AAAM,SAAO,IAAI,QAAQ,EAAE;AAAO;AACjD,CAAC;AAAmB,WAAS;AAAM,eAAa;AAAQ,mBAAiB;AAAe,OAAK;AAAK;AAClG,CAAC;AACC,QAAM,KAAK,IAAI;AAAY,WAAS,IAAI;AAAM,UAAQ;AACtD,cAAY;AAAM,UAAQ,IAAI,OAAO,IAAI;AAAS,iBAAe,IAAI;AAAc,SAAO,IAAI;AAChG;AACA,CAJC,cAIc;AAAS,gBAAc,IAAI;AAAW,SAAO,IAAI;AAAW;AAC3E,CAAC;AACC,QAAM,OAAO,IAAI;AAAY,WAAS,IAAI;AAAM,UAAQ;AACxD,UAAQ,IAAI,MAAM,IAAI;AAAW,iBAAe,IAAI;AACpD,cAAY,IAAI;AAAW,SAAO,IAAI;AACxC;AACA,CALC,YAKY;AACX,UAAQ;AAAS,cAAY;AAAM,SAAO,IAAI;AAAU,gBAAc,IAAI;AAC5E;AAGA,CAhEC;AAgEgB,CAhEhB,eAgEgC;AAAI,sBAAoB;AAAS;","names":[]}
|
|
@@ -14,6 +14,7 @@ function createFloating(className, opts = {}) {
|
|
|
14
14
|
const el = document.createElement("div");
|
|
15
15
|
el.className = className;
|
|
16
16
|
el.style.display = "none";
|
|
17
|
+
el.dataset["shellLayer"] = "";
|
|
17
18
|
(opts.host ?? document.body).appendChild(el);
|
|
18
19
|
let open = false;
|
|
19
20
|
let detach = null;
|
|
@@ -56,5 +57,5 @@ function createFloating(className, opts = {}) {
|
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
export { createFloating };
|
|
59
|
-
//# sourceMappingURL=chunk-
|
|
60
|
-
//# sourceMappingURL=chunk-
|
|
60
|
+
//# sourceMappingURL=chunk-PFERSFRB.js.map
|
|
61
|
+
//# sourceMappingURL=chunk-PFERSFRB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/floating.ts"],"names":[],"mappings":";;;AAqCA,SAAS,eAAe,UAAA,EAAoC;AAC1D,EAAA,MAAM,MAAA,GAAS,MAAY,UAAA,EAAW;AACtC,EAAA,MAAA,CAAO,gBAAA,CAAiB,QAAA,EAAU,MAAA,EAAQ,IAAI,CAAA;AAC9C,EAAA,MAAA,CAAO,gBAAA,CAAiB,UAAU,MAAM,CAAA;AACxC,EAAA,OAAO,MAAM;AAAE,IAAA,MAAA,CAAO,mBAAA,CAAoB,QAAA,EAAU,MAAA,EAAQ,IAAI,CAAA;AAAG,IAAA,MAAA,CAAO,mBAAA,CAAoB,UAAU,MAAM,CAAA;AAAA,EAAG,CAAA;AACnH;AASO,SAAS,cAAA,CAAe,SAAA,EAAmB,IAAA,GAAwB,EAAC,EAAa;AACtF,EAAA,MAAM,EAAA,GAAK,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AAAG,EAAA,EAAA,CAAG,SAAA,GAAY,SAAA;AAAW,EAAA,EAAA,CAAG,MAAM,OAAA,GAAU,MAAA;AACvF,EAAA,EAAA,CAAG,OAAA,CAAQ,YAAY,CAAA,GAAI,EAAA;AAC3B,EAAA,CAAC,IAAA,CAAK,IAAA,IAAQ,QAAA,CAAS,IAAA,EAAM,YAAY,EAAE,CAAA;AAC3C,EAAA,IAAI,IAAA,GAAO,KAAA;AACX,EAAA,IAAI,MAAA,GAA8B,IAAA;AAClC,EAAA,IAAI,OAAA,GAA4C,IAAA;AAChD,EAAA,IAAI,UAAA,GAAa,CAAA;AAEjB,EAAA,MAAM,QAAQ,MAAY;AACxB,IAAA,IAAI,CAAC,IAAA,EAAM;AACX,IAAA,IAAA,GAAO,KAAA;AACP,IAAA,MAAA,IAAS;AAAG,IAAA,MAAA,GAAS,IAAA;AACrB,IAAA,IAAI,OAAA,EAAS;AAAE,MAAA,QAAA,CAAS,mBAAA,CAAoB,WAAA,EAAa,OAAA,EAAS,IAAI,CAAA;AAAG,MAAA,OAAA,GAAU,IAAA;AAAA,IAAM;AACzF,IAAA,MAAM,UAAU,EAAE,UAAA;AAGlB,IAAA,aAAA,CAAc,IAAI,MAAM;AACtB,MAAA,IAAI,YAAY,UAAA,EAAY;AAC5B,MAAA,EAAA,CAAG,MAAM,OAAA,GAAU,MAAA;AAAQ,MAAA,EAAA,CAAG,eAAA,EAAgB;AAAG,MAAA,EAAA,CAAG,SAAA,CAAU,OAAO,SAAS,CAAA;AAAA,IAChF,CAAC,CAAA;AAAA,EACH,CAAA;AAEA,EAAA,MAAM,IAAA,GAAO,CAAC,UAAA,KAAiC;AAC7C,IAAA,UAAA,EAAA;AAAc,IAAA,EAAA,CAAG,SAAA,CAAU,OAAO,SAAS,CAAA;AAC3C,IAAA,IAAA,GAAO,IAAA;AAAM,IAAA,EAAA,CAAG,MAAM,OAAA,GAAU,OAAA;AAAS,IAAA,UAAA,EAAW;AACpD,IAAA,MAAA,KAAW,eAAe,UAAU,CAAA;AAAA,EACtC,CAAA;AAEA,EAAA,MAAM,gBAAA,GAAmB,CAAC,SAAA,EAAuB,QAAA,KAA+C;AAC9F,IAAA,IAAI,OAAA,EAAS;AACb,IAAA,OAAA,GAAU,CAAC,CAAA,KAAwB;AACjC,MAAA,MAAM,IAAI,CAAA,CAAE,MAAA;AACZ,MAAA,IAAI,CAAC,EAAA,CAAG,QAAA,CAAS,CAAC,CAAA,IAAK,EAAE,QAAA,GAAW,CAAC,CAAA,IAAK,KAAA,CAAA,EAAQ,SAAA,EAAU;AAAA,IAC9D,CAAA;AACA,IAAA,QAAA,CAAS,gBAAA,CAAiB,WAAA,EAAa,OAAA,EAAS,IAAI,CAAA;AAAA,EACtD,CAAA;AAEA,EAAA,OAAO,EAAE,EAAA,EAAI,IAAA,EAAM,kBAAkB,KAAA,EAAO,MAAA,EAAQ,MAAM,IAAA,EAAK;AACjE","file":"chunk-PFERSFRB.js","sourcesContent":["// ---------------------------------------------------------------------------\n// The floating-layer lifecycle every popup and menu shares: a body-appended\n// element, display toggling, a scroll / resize follower that keeps it glued to\n// its anchor, a click-away dismiss, and the family's exit motion on close.\n//\n// Patterpad's surface had it (four popups: the cue picker, the slash menu, the\n// action menu, the jump picker) and Storyletter's context menu had the same\n// idea in eleven lines without the follower (ui-review-2026-09, finding 5).\n// This is the surface's, lifted whole. Positioning stays the caller's, because\n// it differs per popup: pass a `reposition` closure to `show()` and the\n// follower re-runs it on scroll and resize until `close()`.\n//\n// The element is REUSED across opens (a popup that re-renders on every\n// keystroke must not tear itself down each time), which is why a stale exit\n// teardown has a token to lose against a reopen.\n// ---------------------------------------------------------------------------\n\nimport { closeWithExit } from \"./exit.js\";\n\nexport interface Floating {\n /** The floating element. Build content into it; it is appended to <body>\n * (or the `host` given), hidden until `show()`. */\n readonly el: HTMLElement;\n /** Show `el` and keep `reposition` glued to the anchor on scroll / resize.\n * Safe to call repeatedly (on every re-render): the follower attaches once. */\n show(reposition: () => void): void;\n /** While open, call `onOutside` on a pointer-down outside `el`, and outside\n * anything `isInside` accepts (a second flyout, the anchor button).\n * Idempotent: arms once. */\n dismissOnOutside(onOutside: () => void, isInside?: (target: Node) => boolean): void;\n /** Play the exit, then hide `el`, clear its children, and drop the follower\n * and any outside listener. */\n close(): void;\n isOpen(): boolean;\n}\n\n/** Re-run `reposition` on any scroll or resize until the returned disposer runs. */\nfunction followOnScroll(reposition: () => void): () => void {\n const onMove = (): void => reposition();\n window.addEventListener(\"scroll\", onMove, true);\n window.addEventListener(\"resize\", onMove);\n return () => { window.removeEventListener(\"scroll\", onMove, true); window.removeEventListener(\"resize\", onMove); };\n}\n\nexport interface FloatingOptions {\n /** Where the element is appended. Default <body>. Under a modal <dialog> a\n * body-level node sits behind the scrim, inert, so a popup opened from\n * inside one passes the dialog here and paints in its top layer. */\n host?: HTMLElement;\n}\n\nexport function createFloating(className: string, opts: FloatingOptions = {}): Floating {\n const el = document.createElement(\"div\"); el.className = className; el.style.display = \"none\";\n el.dataset[\"shellLayer\"] = \"\"; // tokens.css: never a window-drag region, even over one\n (opts.host ?? document.body).appendChild(el);\n let open = false;\n let detach: (() => void) | null = null;\n let outside: ((e: MouseEvent) => void) | null = null;\n let closeToken = 0; // bumped on every show / close so a stale exit teardown cannot clobber a reopen\n\n const close = (): void => {\n if (!open) return;\n open = false;\n detach?.(); detach = null; // stop following + dismissing immediately; it is on its way out\n if (outside) { document.removeEventListener(\"mousedown\", outside, true); outside = null; }\n const myToken = ++closeToken;\n // Play the exit, THEN hide + clear. A reopen during the fade must win:\n // show() bumps closeToken, and this guard abandons the stale teardown.\n closeWithExit(el, () => {\n if (myToken !== closeToken) return;\n el.style.display = \"none\"; el.replaceChildren(); el.classList.remove(\"closing\");\n });\n };\n\n const show = (reposition: () => void): void => {\n closeToken++; el.classList.remove(\"closing\"); // cancel any in-flight exit, undo its class\n open = true; el.style.display = \"block\"; reposition();\n detach ??= followOnScroll(reposition);\n };\n\n const dismissOnOutside = (onOutside: () => void, isInside?: (target: Node) => boolean): void => {\n if (outside) return; // already armed\n outside = (e: MouseEvent): void => {\n const t = e.target as Node;\n if (!el.contains(t) && !(isInside?.(t) ?? false)) onOutside();\n };\n document.addEventListener(\"mousedown\", outside, true);\n };\n\n return { el, show, dismissOnOutside, close, isOpen: () => open };\n}\n"]}
|
package/dist/context-menu.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context-menu.css"],"sourcesContent":["/* The right-click menu and the anchored popover (context-menu.ts), styled\n beside the module that builds them so a tool window that imports the module\n gets the look as well as the behaviour (Storyletter's Links window once got\n an unstyled static div and a scrollbar). Tokens only, so both palettes come\n out right wherever it lands. */\n\n.ctxmenu {\n position: fixed; z-index: 300; min-width: 160px; padding: 4px;\n background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-md);\n box-shadow: var(--shadow-3); display: flex; flex-direction: column; gap: 1px;\n /* The family's panel motion, so reduced motion applies here too. */\n animation: var(--anim-panel-in);\n}\n/* The reset is spelled out here, not inherited from a host stylesheet's\n `button` rule, for the same reason this file exists. */\n.ctxmenu-item {\n text-align: left; width: 100%; padding: 6px 10px;\n font: inherit; font-size: 13.5px; color: var(--ink);\n background: none; border: 0; border-radius: var(--radius-sm); cursor: pointer;\n transition: background var(--dur-quick) var(--ease-standard), color var(--dur-quick) var(--ease-standard);\n}\n.ctxmenu-item:hover { background: var(--accent-soft, color-mix(in oklab, var(--accent) 10%, transparent)); color: var(--accent); }\n/* Greyed and inert, and it must not light up on hover like a live item. */\n.ctxmenu-item:disabled { color: var(--muted); opacity: 0.55; cursor: default; }\n.ctxmenu-item:disabled:hover { background: none; color: var(--muted); }\n.ctxmenu-item.danger { color: var(--danger); }\n.ctxmenu-item.danger:hover { background: var(--danger); color: var(--surface); }\n\n/* An anchored panel: the same floating shell as the menu, hosting content. */\n.popover {\n position: fixed; z-index: 300; padding: 10px;\n background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-md);\n box-shadow: var(--shadow-3);\n animation: var(--anim-panel-in);\n}\n\n/* Exit: .closing swaps the enter keyframes for their reverse (closeWithExit),\n and a node on its way out takes no clicks. */\n.ctxmenu.closing, .popover.closing { animation: var(--anim-panel-out); pointer-events: none; }\n"],"mappings":";AAMA,CAAC;AACC,YAAU;AAAO,WAAS;AAAK,aAAW;AAAO,WAAS;AAC1D,cAAY,IAAI;AAAY,UAAQ,IAAI,MAAM,IAAI;AAAS,iBAAe,IAAI;AAC9E,cAAY,IAAI;AAAa,WAAS;AAAM,kBAAgB;AAAQ,OAAK;AAEzE,aAAW,IAAI;AACjB;AAGA,CAAC;AACC,cAAY;AAAM,SAAO;AAAM,WAAS,IAAI;AAC5C,QAAM;AAAS,aAAW;AAAQ,SAAO,IAAI;AAC7C,cAAY;AAAM,UAAQ;AAAG,iBAAe,IAAI;AAAc,UAAQ;AACtE,cAAY,WAAW,IAAI,aAAa,IAAI,gBAAgB,EAAE,MAAM,IAAI,aAAa,IAAI;AAC3F;AACA,CANC,YAMY;AAAS,cAAY,IAAI,aAAa,EAAE,UAAU,GAAG,KAAK,EAAE,IAAI,UAAU,GAAG,EAAE;AAAe,SAAO,IAAI;AAAW;AAEjI,CARC,YAQY;AAAY,SAAO,IAAI;AAAU,WAAS;AAAM,UAAQ;AAAS;AAC9E,CATC,YASY,SAAS;AAAS,cAAY;AAAM,SAAO,IAAI;AAAU;AACtE,CAVC,YAUY,CAAC;AAAS,SAAO,IAAI;AAAW;AAC7C,CAXC,YAWY,CADC,MACM;AAAS,cAAY,IAAI;AAAW,SAAO,IAAI;AAAY;AAG/E,CAAC;AACC,YAAU;AAAO,WAAS;AAAK,WAAS;AACxC,cAAY,IAAI;AAAY,UAAQ,IAAI,MAAM,IAAI;AAAS,iBAAe,IAAI;AAC9E,cAAY,IAAI;AAChB,aAAW,IAAI;AACjB;AAIA,CAhCC,OAgCO,CAAC;AAAS,CATjB,OASyB,CAAjB;AAA4B,aAAW,IAAI;AAAmB,kBAAgB;AAAM;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/context-menu.css"],"sourcesContent":["/* The right-click menu and the anchored popover (context-menu.ts), styled\n beside the module that builds them so a tool window that imports the module\n gets the look as well as the behaviour (Storyletter's Links window once got\n an unstyled static div and a scrollbar). Tokens only, so both palettes come\n out right wherever it lands. */\n\n.ctxmenu {\n position: fixed; z-index: 300; min-width: 160px; padding: 4px;\n background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-md);\n box-shadow: var(--shadow-3); display: flex; flex-direction: column; gap: 1px;\n /* The family's panel motion, so reduced motion applies here too. */\n animation: var(--anim-panel-in);\n}\n/* The reset is spelled out here, not inherited from a host stylesheet's\n `button` rule, for the same reason this file exists. */\n.ctxmenu-item {\n text-align: left; width: 100%; padding: 6px 10px;\n font: inherit; font-size: 13.5px; color: var(--ink);\n background: none; border: 0; border-radius: var(--radius-sm); cursor: pointer;\n transition: background var(--dur-quick) var(--ease-standard), color var(--dur-quick) var(--ease-standard);\n}\n.ctxmenu-item:hover { background: var(--accent-soft, color-mix(in oklab, var(--accent) 10%, transparent)); color: var(--accent); }\n/* Greyed and inert, and it must not light up on hover like a live item. */\n.ctxmenu-item:disabled { color: var(--muted); opacity: 0.55; cursor: default; }\n.ctxmenu-item:disabled:hover { background: none; color: var(--muted); }\n.ctxmenu-item.danger { color: var(--danger); }\n.ctxmenu-item.danger:hover { background: var(--danger); color: var(--surface); }\n\n/* An anchored panel: the same floating shell as the menu, hosting content. */\n.popover {\n position: fixed; z-index: 300; padding: 10px;\n background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-md);\n box-shadow: var(--shadow-3);\n animation: var(--anim-panel-in);\n}\n\n/* Exit: .closing swaps the enter keyframes for their reverse (closeWithExit),\n and a node on its way out takes no clicks. */\n.ctxmenu.closing, .popover.closing { animation: var(--anim-panel-out); pointer-events: none; }\n\n/* Never a window-drag region, even over one (dialog.css says why). */\n.ctxmenu, .ctxmenu *, .popover, .popover * { -webkit-app-region: no-drag; }\n"],"mappings":";AAMA,CAAC;AACC,YAAU;AAAO,WAAS;AAAK,aAAW;AAAO,WAAS;AAC1D,cAAY,IAAI;AAAY,UAAQ,IAAI,MAAM,IAAI;AAAS,iBAAe,IAAI;AAC9E,cAAY,IAAI;AAAa,WAAS;AAAM,kBAAgB;AAAQ,OAAK;AAEzE,aAAW,IAAI;AACjB;AAGA,CAAC;AACC,cAAY;AAAM,SAAO;AAAM,WAAS,IAAI;AAC5C,QAAM;AAAS,aAAW;AAAQ,SAAO,IAAI;AAC7C,cAAY;AAAM,UAAQ;AAAG,iBAAe,IAAI;AAAc,UAAQ;AACtE,cAAY,WAAW,IAAI,aAAa,IAAI,gBAAgB,EAAE,MAAM,IAAI,aAAa,IAAI;AAC3F;AACA,CANC,YAMY;AAAS,cAAY,IAAI,aAAa,EAAE,UAAU,GAAG,KAAK,EAAE,IAAI,UAAU,GAAG,EAAE;AAAe,SAAO,IAAI;AAAW;AAEjI,CARC,YAQY;AAAY,SAAO,IAAI;AAAU,WAAS;AAAM,UAAQ;AAAS;AAC9E,CATC,YASY,SAAS;AAAS,cAAY;AAAM,SAAO,IAAI;AAAU;AACtE,CAVC,YAUY,CAAC;AAAS,SAAO,IAAI;AAAW;AAC7C,CAXC,YAWY,CADC,MACM;AAAS,cAAY,IAAI;AAAW,SAAO,IAAI;AAAY;AAG/E,CAAC;AACC,YAAU;AAAO,WAAS;AAAK,WAAS;AACxC,cAAY,IAAI;AAAY,UAAQ,IAAI,MAAM,IAAI;AAAS,iBAAe,IAAI;AAC9E,cAAY,IAAI;AAChB,aAAW,IAAI;AACjB;AAIA,CAhCC,OAgCO,CAAC;AAAS,CATjB,OASyB,CAAjB;AAA4B,aAAW,IAAI;AAAmB,kBAAgB;AAAM;AAG7F,CAnCC;AAmCS,CAnCT,QAmCkB;AAAG,CAZrB;AAY+B,CAZ/B,QAYwC;AAAI,sBAAoB;AAAS;","names":[]}
|
package/dist/dialog.css
CHANGED
package/dist/dialog.css.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/dialog.css"],"sourcesContent":["/* ============================================================================\n The dialog frame (dialog.ts): the one panel, the one scrim and the one\n actions row behind every modal in the family. Content classes stay with the\n dialog that owns them (confirm.css, identity.css, about.css,\n notes-editor.css); the frame is here, once.\n\n Reads the token grammar (tokens.css) and the palette (--surface, --ink,\n --line, --muted). The buttons in the actions row wear `.btn` from\n controls.css, which is imported beside this file.\n\n Width is a per-dialog decision, so it is a custom property the content\n class sets (`.confirm-dialog { --dialog-width: 384px }`) rather than a rule\n the content class has to out-specify.\n ============================================================================ */\n\n.shell-dialog {\n --dialog-width: 420px;\n width: min(var(--dialog-width), 92vw); max-height: 82vh;\n padding: 18px 20px;\n border: 1px solid var(--line); border-radius: var(--radius-lg);\n background: var(--surface); color: var(--ink);\n box-shadow: var(--shadow-3);\n /* The chrome base (tokens.css): unstyled text in a dialog lands at 14px\n whatever the app's reading root is. */\n font-family: var(--font-ui); font-size: var(--chrome-size);\n display: flex; flex-direction: column; gap: 10px;\n animation: var(--anim-panel-in);\n}\n/* The one scrim: ink-derived so it stays palette-aware, and blurred so the\n window behind reads as behind. */\n.shell-dialog::backdrop {\n background: var(--scrim);\n backdrop-filter: blur(3px);\n animation: var(--anim-backdrop-in);\n}\n/* Exit: .closing swaps the enter keyframes for their reverse (closeWithExit),\n and the fading panel must not catch a stray click. */\n.shell-dialog.closing { animation: var(--anim-panel-out); pointer-events: none; }\n.shell-dialog.closing::backdrop { animation: var(--anim-backdrop-out); }\n\n/* A heading is sentence-case text in the UI face, weight 600: how a group box\n reads on the platform, not an overline. */\n.shell-dialog-title { margin: 0; font: 600 16px/1.3 var(--font-ui); }\n.shell-dialog-sub { margin: -4px 0 0; font: 13px/1.45 var(--font-ui); color: var(--muted); }\n.shell-dialog-body { display: flex; flex-direction: column; gap: 8px; min-height: 0; overflow-y: auto; }\n.shell-dialog-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 6px; }\n.shell-dialog-actions:empty { display: none; }\n"],"mappings":";AAeA,CAAC;AACC,kBAAgB;AAChB,SAAO,IAAI,IAAI,eAAe,EAAE;AAAO,cAAY;AACnD,WAAS,KAAK;AACd,UAAQ,IAAI,MAAM,IAAI;AAAS,iBAAe,IAAI;AAClD,cAAY,IAAI;AAAY,SAAO,IAAI;AACvC,cAAY,IAAI;AAGhB,eAAa,IAAI;AAAY,aAAW,IAAI;AAC5C,WAAS;AAAM,kBAAgB;AAAQ,OAAK;AAC5C,aAAW,IAAI;AACjB;AAGA,CAfC,YAeY;AACX,cAAY,IAAI;AAChB,mBAAiB,KAAK;AACtB,aAAW,IAAI;AACjB;AAGA,CAtBC,YAsBY,CAAC;AAAU,aAAW,IAAI;AAAmB,kBAAgB;AAAM;AAChF,CAvBC,YAuBY,CADC,OACO;AAAa,aAAW,IAAI;AAAsB;
|
|
1
|
+
{"version":3,"sources":["../src/dialog.css"],"sourcesContent":["/* ============================================================================\n The dialog frame (dialog.ts): the one panel, the one scrim and the one\n actions row behind every modal in the family. Content classes stay with the\n dialog that owns them (confirm.css, identity.css, about.css,\n notes-editor.css); the frame is here, once.\n\n Reads the token grammar (tokens.css) and the palette (--surface, --ink,\n --line, --muted). The buttons in the actions row wear `.btn` from\n controls.css, which is imported beside this file.\n\n Width is a per-dialog decision, so it is a custom property the content\n class sets (`.confirm-dialog { --dialog-width: 384px }`) rather than a rule\n the content class has to out-specify.\n ============================================================================ */\n\n.shell-dialog {\n --dialog-width: 420px;\n width: min(var(--dialog-width), 92vw); max-height: 82vh;\n padding: 18px 20px;\n border: 1px solid var(--line); border-radius: var(--radius-lg);\n background: var(--surface); color: var(--ink);\n box-shadow: var(--shadow-3);\n /* The chrome base (tokens.css): unstyled text in a dialog lands at 14px\n whatever the app's reading root is. */\n font-family: var(--font-ui); font-size: var(--chrome-size);\n display: flex; flex-direction: column; gap: 10px;\n animation: var(--anim-panel-in);\n}\n/* The one scrim: ink-derived so it stays palette-aware, and blurred so the\n window behind reads as behind. */\n.shell-dialog::backdrop {\n background: var(--scrim);\n backdrop-filter: blur(3px);\n animation: var(--anim-backdrop-in);\n}\n/* Exit: .closing swaps the enter keyframes for their reverse (closeWithExit),\n and the fading panel must not catch a stray click. */\n.shell-dialog.closing { animation: var(--anim-panel-out); pointer-events: none; }\n.shell-dialog.closing::backdrop { animation: var(--anim-backdrop-out); }\n\n/* Never a window-drag region, even where it lies over one. Electron hands a click inside a\n `-webkit-app-region: drag` area to the OS as a window drag before the page sees it, and a modal\n making the page behind it inert does not change that, so a layer drawn over the welcome\n screen's whole-window drag region lost every click while the keyboard still worked\n (2026-09-28: the first-run identity's Continue and About's Close). Reproduced in a real\n Electron window; `no-drag` on the layer itself is the fix. */\n.shell-dialog, .shell-dialog * { -webkit-app-region: no-drag; }\n\n/* A heading is sentence-case text in the UI face, weight 600: how a group box\n reads on the platform, not an overline. */\n.shell-dialog-title { margin: 0; font: 600 16px/1.3 var(--font-ui); }\n.shell-dialog-sub { margin: -4px 0 0; font: 13px/1.45 var(--font-ui); color: var(--muted); }\n.shell-dialog-body { display: flex; flex-direction: column; gap: 8px; min-height: 0; overflow-y: auto; }\n.shell-dialog-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 6px; }\n.shell-dialog-actions:empty { display: none; }\n"],"mappings":";AAeA,CAAC;AACC,kBAAgB;AAChB,SAAO,IAAI,IAAI,eAAe,EAAE;AAAO,cAAY;AACnD,WAAS,KAAK;AACd,UAAQ,IAAI,MAAM,IAAI;AAAS,iBAAe,IAAI;AAClD,cAAY,IAAI;AAAY,SAAO,IAAI;AACvC,cAAY,IAAI;AAGhB,eAAa,IAAI;AAAY,aAAW,IAAI;AAC5C,WAAS;AAAM,kBAAgB;AAAQ,OAAK;AAC5C,aAAW,IAAI;AACjB;AAGA,CAfC,YAeY;AACX,cAAY,IAAI;AAChB,mBAAiB,KAAK;AACtB,aAAW,IAAI;AACjB;AAGA,CAtBC,YAsBY,CAAC;AAAU,aAAW,IAAI;AAAmB,kBAAgB;AAAM;AAChF,CAvBC,YAuBY,CADC,OACO;AAAa,aAAW,IAAI;AAAsB;AAQvE,CA/BC;AA+Bc,CA/Bd,aA+B4B;AAAI,sBAAoB;AAAS;AAI9D,CAAC;AAAqB,UAAQ;AAAG,QAAM,IAAI,IAAI,CAAC,IAAI,IAAI;AAAY;AACpE,CAAC;AAAmB,UAAQ,KAAK,EAAE;AAAG,QAAM,IAAI,CAAC,KAAK,IAAI;AAAY,SAAO,IAAI;AAAU;AAC3F,CAAC;AAAoB,WAAS;AAAM,kBAAgB;AAAQ,OAAK;AAAK,cAAY;AAAG,cAAY;AAAM;AACvG,CAAC;AAAuB,WAAS;AAAM,mBAAiB;AAAU,OAAK;AAAK,cAAY;AAAK;AAC7F,CADC,oBACoB;AAAS,WAAS;AAAM;","names":[]}
|
package/dist/floating.cjs
CHANGED
|
@@ -37,6 +37,7 @@ function createFloating(className, opts = {}) {
|
|
|
37
37
|
const el = document.createElement("div");
|
|
38
38
|
el.className = className;
|
|
39
39
|
el.style.display = "none";
|
|
40
|
+
el.dataset["shellLayer"] = "";
|
|
40
41
|
(opts.host ?? document.body).appendChild(el);
|
|
41
42
|
let open = false;
|
|
42
43
|
let detach = null;
|
package/dist/floating.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/exit.ts","../src/floating.ts"],"names":[],"mappings":"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","file":"floating.cjs","sourcesContent":["// ---------------------------------------------------------------------------\n// The exit half of the panel motion vocabulary.\n//\n// Enters are pure CSS: they run on insertion. Exits cannot be, because the node\n// has to OUTLIVE the close for the animation to play and then be torn down. So\n// every closing surface routes its teardown through here instead of removing\n// itself directly.\n//\n// Patterpad's `closeWithExit`, lifted. It was already app-agnostic, and the\n// shell had grown a second copy of the same twelve lines inside `confirm.ts`,\n// which is the drift this package exists to stop: one motion vocabulary, drawn\n// one way. `tokens.css` already carried the keyframes and the durations; this\n// is the missing half that plays them.\n// ---------------------------------------------------------------------------\n\n/**\n * Play the close animation on `element`, then run `done` exactly once.\n *\n * `done` does the real teardown: remove the node, drop listeners, unmount\n * whatever was inside it. Under `prefers-reduced-motion` the duration reads 0\n * and `done` runs synchronously, so reduced motion means instant rather than\n * an invisible wait.\n */\nexport function closeWithExit(element: HTMLElement, done: () => void): void {\n element.classList.add(\"closing\");\n // getComputedStyle flushes pending style, so this reflects the `.closing`\n // rule we just added rather than the state before it.\n const seconds = parseFloat(getComputedStyle(element).animationDuration) || 0;\n if (seconds === 0) { done(); return; }\n\n let fired = false;\n const finish = (): void => {\n if (fired) return;\n fired = true;\n element.removeEventListener(\"animationend\", onEnd);\n clearTimeout(timer);\n done();\n };\n // Only OUR animation ending counts. Content inside the panel (rows, pills,\n // spinners) raises its own animationend, and an inner one firing first would\n // tear the panel down mid-fade.\n const onEnd = (e: AnimationEvent): void => { if (e.target === element) finish(); };\n element.addEventListener(\"animationend\", onEnd);\n // Fallback: animationend is missed if the element is hidden or the animation\n // is interrupted, and a panel that never tears down is worse than an abrupt one.\n const timer = setTimeout(finish, seconds * 1000 + 120);\n}\n","// ---------------------------------------------------------------------------\n// The floating-layer lifecycle every popup and menu shares: a body-appended\n// element, display toggling, a scroll / resize follower that keeps it glued to\n// its anchor, a click-away dismiss, and the family's exit motion on close.\n//\n// Patterpad's surface had it (four popups: the cue picker, the slash menu, the\n// action menu, the jump picker) and Storyletter's context menu had the same\n// idea in eleven lines without the follower (ui-review-2026-09, finding 5).\n// This is the surface's, lifted whole. Positioning stays the caller's, because\n// it differs per popup: pass a `reposition` closure to `show()` and the\n// follower re-runs it on scroll and resize until `close()`.\n//\n// The element is REUSED across opens (a popup that re-renders on every\n// keystroke must not tear itself down each time), which is why a stale exit\n// teardown has a token to lose against a reopen.\n// ---------------------------------------------------------------------------\n\nimport { closeWithExit } from \"./exit.js\";\n\nexport interface Floating {\n /** The floating element. Build content into it; it is appended to <body>\n * (or the `host` given), hidden until `show()`. */\n readonly el: HTMLElement;\n /** Show `el` and keep `reposition` glued to the anchor on scroll / resize.\n * Safe to call repeatedly (on every re-render): the follower attaches once. */\n show(reposition: () => void): void;\n /** While open, call `onOutside` on a pointer-down outside `el`, and outside\n * anything `isInside` accepts (a second flyout, the anchor button).\n * Idempotent: arms once. */\n dismissOnOutside(onOutside: () => void, isInside?: (target: Node) => boolean): void;\n /** Play the exit, then hide `el`, clear its children, and drop the follower\n * and any outside listener. */\n close(): void;\n isOpen(): boolean;\n}\n\n/** Re-run `reposition` on any scroll or resize until the returned disposer runs. */\nfunction followOnScroll(reposition: () => void): () => void {\n const onMove = (): void => reposition();\n window.addEventListener(\"scroll\", onMove, true);\n window.addEventListener(\"resize\", onMove);\n return () => { window.removeEventListener(\"scroll\", onMove, true); window.removeEventListener(\"resize\", onMove); };\n}\n\nexport interface FloatingOptions {\n /** Where the element is appended. Default <body>. Under a modal <dialog> a\n * body-level node sits behind the scrim, inert, so a popup opened from\n * inside one passes the dialog here and paints in its top layer. */\n host?: HTMLElement;\n}\n\nexport function createFloating(className: string, opts: FloatingOptions = {}): Floating {\n const el = document.createElement(\"div\"); el.className = className; el.style.display = \"none\";\n (opts.host ?? document.body).appendChild(el);\n let open = false;\n let detach: (() => void) | null = null;\n let outside: ((e: MouseEvent) => void) | null = null;\n let closeToken = 0; // bumped on every show / close so a stale exit teardown cannot clobber a reopen\n\n const close = (): void => {\n if (!open) return;\n open = false;\n detach?.(); detach = null; // stop following + dismissing immediately; it is on its way out\n if (outside) { document.removeEventListener(\"mousedown\", outside, true); outside = null; }\n const myToken = ++closeToken;\n // Play the exit, THEN hide + clear. A reopen during the fade must win:\n // show() bumps closeToken, and this guard abandons the stale teardown.\n closeWithExit(el, () => {\n if (myToken !== closeToken) return;\n el.style.display = \"none\"; el.replaceChildren(); el.classList.remove(\"closing\");\n });\n };\n\n const show = (reposition: () => void): void => {\n closeToken++; el.classList.remove(\"closing\"); // cancel any in-flight exit, undo its class\n open = true; el.style.display = \"block\"; reposition();\n detach ??= followOnScroll(reposition);\n };\n\n const dismissOnOutside = (onOutside: () => void, isInside?: (target: Node) => boolean): void => {\n if (outside) return; // already armed\n outside = (e: MouseEvent): void => {\n const t = e.target as Node;\n if (!el.contains(t) && !(isInside?.(t) ?? false)) onOutside();\n };\n document.addEventListener(\"mousedown\", outside, true);\n };\n\n return { el, show, dismissOnOutside, close, isOpen: () => open };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/exit.ts","../src/floating.ts"],"names":[],"mappings":"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","file":"floating.cjs","sourcesContent":["// ---------------------------------------------------------------------------\n// The exit half of the panel motion vocabulary.\n//\n// Enters are pure CSS: they run on insertion. Exits cannot be, because the node\n// has to OUTLIVE the close for the animation to play and then be torn down. So\n// every closing surface routes its teardown through here instead of removing\n// itself directly.\n//\n// Patterpad's `closeWithExit`, lifted. It was already app-agnostic, and the\n// shell had grown a second copy of the same twelve lines inside `confirm.ts`,\n// which is the drift this package exists to stop: one motion vocabulary, drawn\n// one way. `tokens.css` already carried the keyframes and the durations; this\n// is the missing half that plays them.\n// ---------------------------------------------------------------------------\n\n/**\n * Play the close animation on `element`, then run `done` exactly once.\n *\n * `done` does the real teardown: remove the node, drop listeners, unmount\n * whatever was inside it. Under `prefers-reduced-motion` the duration reads 0\n * and `done` runs synchronously, so reduced motion means instant rather than\n * an invisible wait.\n */\nexport function closeWithExit(element: HTMLElement, done: () => void): void {\n element.classList.add(\"closing\");\n // getComputedStyle flushes pending style, so this reflects the `.closing`\n // rule we just added rather than the state before it.\n const seconds = parseFloat(getComputedStyle(element).animationDuration) || 0;\n if (seconds === 0) { done(); return; }\n\n let fired = false;\n const finish = (): void => {\n if (fired) return;\n fired = true;\n element.removeEventListener(\"animationend\", onEnd);\n clearTimeout(timer);\n done();\n };\n // Only OUR animation ending counts. Content inside the panel (rows, pills,\n // spinners) raises its own animationend, and an inner one firing first would\n // tear the panel down mid-fade.\n const onEnd = (e: AnimationEvent): void => { if (e.target === element) finish(); };\n element.addEventListener(\"animationend\", onEnd);\n // Fallback: animationend is missed if the element is hidden or the animation\n // is interrupted, and a panel that never tears down is worse than an abrupt one.\n const timer = setTimeout(finish, seconds * 1000 + 120);\n}\n","// ---------------------------------------------------------------------------\n// The floating-layer lifecycle every popup and menu shares: a body-appended\n// element, display toggling, a scroll / resize follower that keeps it glued to\n// its anchor, a click-away dismiss, and the family's exit motion on close.\n//\n// Patterpad's surface had it (four popups: the cue picker, the slash menu, the\n// action menu, the jump picker) and Storyletter's context menu had the same\n// idea in eleven lines without the follower (ui-review-2026-09, finding 5).\n// This is the surface's, lifted whole. Positioning stays the caller's, because\n// it differs per popup: pass a `reposition` closure to `show()` and the\n// follower re-runs it on scroll and resize until `close()`.\n//\n// The element is REUSED across opens (a popup that re-renders on every\n// keystroke must not tear itself down each time), which is why a stale exit\n// teardown has a token to lose against a reopen.\n// ---------------------------------------------------------------------------\n\nimport { closeWithExit } from \"./exit.js\";\n\nexport interface Floating {\n /** The floating element. Build content into it; it is appended to <body>\n * (or the `host` given), hidden until `show()`. */\n readonly el: HTMLElement;\n /** Show `el` and keep `reposition` glued to the anchor on scroll / resize.\n * Safe to call repeatedly (on every re-render): the follower attaches once. */\n show(reposition: () => void): void;\n /** While open, call `onOutside` on a pointer-down outside `el`, and outside\n * anything `isInside` accepts (a second flyout, the anchor button).\n * Idempotent: arms once. */\n dismissOnOutside(onOutside: () => void, isInside?: (target: Node) => boolean): void;\n /** Play the exit, then hide `el`, clear its children, and drop the follower\n * and any outside listener. */\n close(): void;\n isOpen(): boolean;\n}\n\n/** Re-run `reposition` on any scroll or resize until the returned disposer runs. */\nfunction followOnScroll(reposition: () => void): () => void {\n const onMove = (): void => reposition();\n window.addEventListener(\"scroll\", onMove, true);\n window.addEventListener(\"resize\", onMove);\n return () => { window.removeEventListener(\"scroll\", onMove, true); window.removeEventListener(\"resize\", onMove); };\n}\n\nexport interface FloatingOptions {\n /** Where the element is appended. Default <body>. Under a modal <dialog> a\n * body-level node sits behind the scrim, inert, so a popup opened from\n * inside one passes the dialog here and paints in its top layer. */\n host?: HTMLElement;\n}\n\nexport function createFloating(className: string, opts: FloatingOptions = {}): Floating {\n const el = document.createElement(\"div\"); el.className = className; el.style.display = \"none\";\n el.dataset[\"shellLayer\"] = \"\"; // tokens.css: never a window-drag region, even over one\n (opts.host ?? document.body).appendChild(el);\n let open = false;\n let detach: (() => void) | null = null;\n let outside: ((e: MouseEvent) => void) | null = null;\n let closeToken = 0; // bumped on every show / close so a stale exit teardown cannot clobber a reopen\n\n const close = (): void => {\n if (!open) return;\n open = false;\n detach?.(); detach = null; // stop following + dismissing immediately; it is on its way out\n if (outside) { document.removeEventListener(\"mousedown\", outside, true); outside = null; }\n const myToken = ++closeToken;\n // Play the exit, THEN hide + clear. A reopen during the fade must win:\n // show() bumps closeToken, and this guard abandons the stale teardown.\n closeWithExit(el, () => {\n if (myToken !== closeToken) return;\n el.style.display = \"none\"; el.replaceChildren(); el.classList.remove(\"closing\");\n });\n };\n\n const show = (reposition: () => void): void => {\n closeToken++; el.classList.remove(\"closing\"); // cancel any in-flight exit, undo its class\n open = true; el.style.display = \"block\"; reposition();\n detach ??= followOnScroll(reposition);\n };\n\n const dismissOnOutside = (onOutside: () => void, isInside?: (target: Node) => boolean): void => {\n if (outside) return; // already armed\n outside = (e: MouseEvent): void => {\n const t = e.target as Node;\n if (!el.contains(t) && !(isInside?.(t) ?? false)) onOutside();\n };\n document.addEventListener(\"mousedown\", outside, true);\n };\n\n return { el, show, dismissOnOutside, close, isOpen: () => open };\n}\n"]}
|
package/dist/floating.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -1489,6 +1489,7 @@ function createFloating(className, opts = {}) {
|
|
|
1489
1489
|
const el2 = document.createElement("div");
|
|
1490
1490
|
el2.className = className;
|
|
1491
1491
|
el2.style.display = "none";
|
|
1492
|
+
el2.dataset["shellLayer"] = "";
|
|
1492
1493
|
(opts.host ?? document.body).appendChild(el2);
|
|
1493
1494
|
let open = false;
|
|
1494
1495
|
let detach = null;
|