@taprootio/espalier 2.10.0 → 2.11.0

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.
@@ -2,12 +2,14 @@ import { type PropertyValues } from "lit";
2
2
  import { EspalierElementBase } from "../shared/esp-element-base.js";
3
3
  import { type FlyoutCloseReason } from "../shared/flyout-events.js";
4
4
  /**
5
- * A transient panel for help, more-info, and preview content that
6
- * claims designed spare width before ever covering the page.
5
+ * A transient panel for help and more-info content that claims designed spare
6
+ * width before ever covering the page.
7
7
  *
8
8
  * Place one `esp-flyout` in an `esp-page`'s `flyout` slot. The panel
9
- * lives on the canvas, outside the content surface. The page decides
10
- * where it goes, from widest viewport to narrowest:
9
+ * lives on the canvas after any persistent preview, outside the content
10
+ * surface. It negotiates between `--esp-page-flyout-min-width` and
11
+ * `--esp-page-flyout-max-width`; preview receives flexible room first. The page
12
+ * decides where help goes, from widest viewport to narrowest:
11
13
  *
12
14
  * 1. **Gutter** — the panel occupies the right canvas gutter. When the
13
15
  * gutter already has room, it claims existing spare canvas and the
@@ -18,19 +20,25 @@ import { type FlyoutCloseReason } from "../shared/flyout-events.js";
18
20
  * 3. **Docked sidebar** — once the gutter is gone, the panel competes
19
21
  * for width with the main well; content narrows but is never
20
22
  * covered.
21
- * 4. **Overlay drawer** — below the mobile threshold (`50em`, the
22
- * same one the `esp-menu` drawer uses) the panel becomes a fixed
23
+ * 4. **Overlay drawer** — when the complete sidebar + main + help
24
+ * minimums do not fit, or below the mobile threshold (`50em`, the
25
+ * same one the `esp-menu` drawer uses), the panel becomes a fixed
23
26
  * drawer over a vellum backdrop. Set `mode="overlay"` to get this
24
27
  * presentation at every width.
25
28
  *
26
29
  * The panel is styled as a tear-off piece: a dotted perforation on
27
30
  * its leading edge, rounded trailing corners, and no shadow — all
28
31
  * overridable through the `--esp-flyout-*` tokens below.
32
+ * An anchored panel shown with preview keeps its caret at the main/preview
33
+ * edge and receives a non-interactive dotted bridge across preview from the
34
+ * page. A short, thick terminus on the preview/help seam completes that
35
+ * connection; no consumer positioning is required.
29
36
  *
30
37
  * A11y follows the presentation. In the in-grid modes (1–3) the
31
38
  * flyout is a non-modal `complementary` landmark named by its
32
- * `heading`, and opening it never steals focus. In the overlay-drawer
33
- * mode (4) it is a true modal: `role="dialog"` + `aria-modal`, the
39
+ * `heading`, opening it never steals focus, and boundary scrolling chains
40
+ * back to the main document. In the overlay-drawer mode (4) it is a true
41
+ * modal: `role="dialog"` + `aria-modal`, the
34
42
  * background goes inert, scroll is locked, focus moves into the drawer
35
43
  * and is trapped, and focus is restored on close. `Escape` closes it
36
44
  * in every mode.
@@ -38,7 +46,7 @@ import { type FlyoutCloseReason } from "../shared/flyout-events.js";
38
46
  * ```html
39
47
  * <style>
40
48
  * esp-page.flyout-demo {
41
- * --esp-page-max-width: 420px;
49
+ * --esp-page-main-max-width: 420px;
42
50
  * --esp-page-canvas-background: var(--esp-color-layer-1);
43
51
  * &::part(wrapper) {
44
52
  * min-height: 320px;
@@ -72,7 +80,7 @@ import { type FlyoutCloseReason } from "../shared/flyout-events.js";
72
80
  * ```html
73
81
  * <style>
74
82
  * esp-page.flyout-match-demo {
75
- * --esp-page-max-width: 420px;
83
+ * --esp-page-main-max-width: 420px;
76
84
  * &::part(wrapper) {
77
85
  * min-height: 320px;
78
86
  * height: 320px;
@@ -317,8 +325,8 @@ export declare class EspalierFlyout extends EspalierElementBase {
317
325
  * Placement behavior.
318
326
  *
319
327
  * - `"auto"` (default) — the page's placement ladder: gutter,
320
- * docked sidebar, then overlay drawer below the `50em` viewport
321
- * threshold.
328
+ * docked sidebar, then overlay drawer when the complete in-grid
329
+ * minimums do not fit or below the `50em` viewport threshold.
322
330
  * - `"overlay"` — always the fixed overlay drawer, at any width.
323
331
  * The page never reserves a grid track for an overlay-mode
324
332
  * flyout.
@@ -1,4 +1,4 @@
1
- var h=function(d,e,t,i){var o=arguments.length,s=o<3?e:i===null?i=Object.getOwnPropertyDescriptor(e,t):i,a;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")s=Reflect.decorate(d,e,t,i);else for(var r=d.length-1;r>=0;r--)(a=d[r])&&(s=(o<3?a(s):o>3?a(e,t,s):a(e,t))||s);return o>3&&s&&Object.defineProperty(e,t,s),s};import{css as p,html as w,nothing as S}from"lit";import{customElement as F,property as l}from"lit/decorators.js";import{classMap as L}from"lit/directives/class-map.js";import{createRef as M,ref as O}from"lit/directives/ref.js";import{EspalierElementBase as A}from"../shared/esp-element-base.js";import{getEspBus as f}from"../shared/bus-events.js";import{FLYOUT_FULL_HEIGHT_REQUEST_EVENT as k,markFlyoutRequestServiced as _}from"../shared/flyout-events.js";import{OverlayController as G}from"../shared/overlay-controller.js";import{cancelSVG as B}from"../shared/svgs/cancel.js";import{traverseToClosest as y}from"../shared/utilities.js";const H="(max-width: 50em)",q="Flyout",R=p`
1
+ var l=function(d,e,t,r){var o=arguments.length,s=o<3?e:r===null?r=Object.getOwnPropertyDescriptor(e,t):r,n;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")s=Reflect.decorate(d,e,t,r);else for(var i=d.length-1;i>=0;i--)(n=d[i])&&(s=(o<3?n(s):o>3?n(e,t,s):n(e,t))||s);return o>3&&s&&Object.defineProperty(e,t,s),s};import{css as p,html as w,nothing as T}from"lit";import{customElement as O,property as h}from"lit/decorators.js";import{classMap as _}from"lit/directives/class-map.js";import{createRef as F,ref as L}from"lit/directives/ref.js";import{EspalierElementBase as k}from"../shared/esp-element-base.js";import{getEspBus as f}from"../shared/bus-events.js";import{FLYOUT_FULL_HEIGHT_REQUEST_EVENT as A,markFlyoutRequestServiced as M}from"../shared/flyout-events.js";import{OverlayController as q}from"../shared/overlay-controller.js";import{cancelSVG as G}from"../shared/svgs/cancel.js";import{traverseToClosest as v}from"../shared/utilities.js";const B="(max-width: 50em)",H="Flyout",R=p`
2
2
  position: fixed;
3
3
  inset: 0;
4
4
  margin-block-start: 0;
@@ -13,13 +13,13 @@ var h=function(d,e,t,i){var o=arguments.length,s=o<3?e:i===null?i=Object.getOwnP
13
13
  inset: 0;
14
14
  background-color: var(--esp-vellum-background, var(--esp-color-layer-3));
15
15
  opacity: var(--esp-vellum-opacity, 0.85);
16
- `,P=p`
16
+ `,z=p`
17
17
  position: absolute;
18
18
  inset-block: 0;
19
19
  inset-inline-end: 0;
20
20
  block-size: auto;
21
21
  max-block-size: none;
22
- width: min(var(--esp-page-flyout-width, 20rem), 85vw);
22
+ width: min(var(--esp-page-flyout-width, var(--esp-page-flyout-max-width, 30rem)), 85vw);
23
23
  overflow-y: auto;
24
24
 
25
25
  border-start-end-radius: 0;
@@ -31,25 +31,26 @@ var h=function(d,e,t,i){var o=arguments.length,s=o<3?e:i===null?i=Object.getOwnP
31
31
  @starting-style {
32
32
  translate: 100% 0;
33
33
  }
34
- `;let n=class extends A{constructor(){super(...arguments),this.open=!1,this.heading="",this.mode="auto",this.fullHeight=!1,this.matchSurface=!1,this.standalone=!1,this.scope="outermost",this.lastGeneratedAriaLabel=null,this.boundKeydown=e=>this.handleKeydown(e),this.boundShowRequest=e=>this.handleShowRequest(e),this.boundCloseRequest=()=>this.close(),this.boundOverlayMediaChange=()=>{this.syncOverlayModal(),this.syncAnchorTracking(),this.syncAnchorGeometry()},this.boundAnchorViewportChange=()=>this.scheduleAnchorGeometrySync(),this.boundFullHeightRequest=e=>{e.stopPropagation(),this.fullHeight=!0},this.anchorPositionRaf=0,this.trackingAnchorPosition=!1,this.busSubscribed=!1,this.panelRef=M(),this.overlay=new G({host:this,getFocusTrapContainer:()=>this.panelRef.value??null,getFocusFallback:()=>this.panelRef.value??null,promote:!1}),this.overlayActive=!1,this.baseRole="complementary"}get effectiveOverlay(){return this.mode==="overlay"||(this.overlayMediaQuery?.matches??!1)}connectedCallback(){super.connectedCallback(),this.hasAttribute("role")||this.setAttribute("role","complementary"),this.baseRole=this.getAttribute("role")??"complementary",!this.overlayMediaQuery&&typeof window<"u"&&"matchMedia"in window&&(this.overlayMediaQuery=window.matchMedia(H)),this.overlayMediaQuery?.addEventListener("change",this.boundOverlayMediaChange),this.syncBusSubscription(),document.addEventListener("keydown",this.boundKeydown),this.addEventListener(k,this.boundFullHeightRequest),this.syncOverlayModal(),this.syncAnchorTracking(),this.syncAnchorGeometry()}disconnectedCallback(){super.disconnectedCallback(),this.unsubscribeBus(),this.overlayMediaQuery?.removeEventListener("change",this.boundOverlayMediaChange),document.removeEventListener("keydown",this.boundKeydown),this.removeEventListener(k,this.boundFullHeightRequest),this.stopAnchorTracking(),this.overlayActive&&(this.overlayActive=!1,this.setAttribute("role",this.baseRole),this.removeAttribute("aria-modal"),this.updateGeneratedAriaLabel()),this.overlayReturnFocusTo=void 0}syncBusSubscription(){if(this.standalone||!this.isConnected){this.unsubscribeBus();return}this.busSubscribed||(f().subscribe("show-flyout",this.boundShowRequest),f().subscribe("close-flyout",this.boundCloseRequest),this.busSubscribed=!0)}unsubscribeBus(){this.busSubscribed&&(f().unsubscribe("show-flyout",this.boundShowRequest),f().unsubscribe("close-flyout",this.boundCloseRequest),this.busSubscribed=!1)}show(){this.open||(this.open=!0,this.dispatchEvent(new CustomEvent("flyout-opened",{detail:{},bubbles:!0,composed:!0})))}close(e="programmatic"){if(this.open){if(this.open=!1,this.overlayActive)this.overlayReturnFocusTo=this.returnFocusTo;else{const t=this.focusIsInside();this.overlay.close(),t&&this.returnFocusTo?.focus()}this.returnFocusTo=void 0,this.dispatchEvent(new CustomEvent("flyout-closed",{detail:{reason:e},bubbles:!0,composed:!0}))}}toggle(){this.open?this.close():this.show()}handleShowRequest(e){this.shouldHandleShowRequest(e)&&(_(e),e.heading!==void 0&&(this.heading=e.heading),e.content!==void 0&&this.replaceChildren(typeof e.content=="string"?document.createTextNode(e.content):e.content),this.anchor=e.anchor,this.fullHeight=e.fullHeight??!1,this.returnFocusTo=e.returnFocusTo,this.show())}shouldHandleShowRequest(e){const t=y(this,"esp-page"),i=e.anchor??e.returnFocusTo;if(!i){if(!t)return!0;const r=this.getPageChain(t);return this.scope==="outermost"&&r[r.length-1]===t}const o=this.getPageChain(i);if(o.length===0)return t===null;const s=o.find(r=>this.hasNearestScopeFlyout(r)),a=s??o[o.length-1];return t!==a?!1:s?this.scope==="nearest":this.scope==="outermost"}getPageChain(e){const t=[];let i=e;for(;i;){const o=y(i,"esp-page");if(!o||t.includes(o))break;t.push(o),i=this.composedParent(o)}return t}hasNearestScopeFlyout(e){return Array.from(e.querySelectorAll("esp-flyout")).some(t=>!t.standalone&&t.scope==="nearest"&&y(t,"esp-page")===e)}handleKeydown(e){if(this.open){if(this.overlayActive&&e.key==="Tab"){this.overlay.trapFocus(e);return}e.key!=="Escape"||e.defaultPrevented||(e.preventDefault(),this.close("escape"))}}focusIsInside(){const e=document.activeElement;return e&&this.contains(e)?!0:this.shadowRoot?.activeElement!=null}updated(e){super.updated(e),e.has("standalone")&&this.syncBusSubscription(),e.has("heading")&&this.updateGeneratedAriaLabel(),(e.has("anchor")||e.has("fullHeight")||e.has("open")||e.has("mode"))&&(this.syncAnchorTracking(),this.syncAnchorGeometry()),(e.has("open")||e.has("mode"))&&this.syncOverlayModal(),(e.has("open")||e.has("mode")||e.has("anchor"))&&this.dispatchEvent(new CustomEvent("flyout-state-changed",{detail:{},bubbles:!0,composed:!0}))}syncAnchorGeometry(){const e=y(this,"esp-page"),t=this.anchor?this.getPageChain(this.anchor):[],i=e?.shadowRoot?.querySelector(".esp-page-main"),o=this.panelRef.value;if(!this.open||this.effectiveOverlay||!this.anchor||!e||!t.includes(e)||!i||!o){this.clearAnchorGeometry();return}const s=this.anchor.getBoundingClientRect(),a=Math.max(0,s.top-i.getBoundingClientRect().top),r=this.getVisibleBlockBounds(),u=Math.max(0,r.bottom-r.top),c=Number.isFinite(s.height)?s.height:0;this.setGeometryProperty("--_esp-flyout-anchor-offset",`${a}px`),this.setGeometryProperty("--_esp-flyout-max-block-size",`${u}px`),this.setGeometryProperty("--_esp-flyout-anchor-block-size",`${c}px`);const v=o.getBoundingClientRect().height||u,b=Math.max(0,s.top+v-r.bottom),C=Math.max(0,v-c),g=this.fullHeight?b:Math.min(b,C);this.setGeometryProperty("--_esp-flyout-viewport-shift",`${g}px`);const z=s.top-g,m=8,E=s.top+c/2,T=Math.min(Math.max(m,v-m),Math.max(m,E-z));this.setGeometryProperty("--_esp-flyout-anchor-marker-offset",`${T}px`)}getVisibleBlockBounds(){const e=window.visualViewport;let t=e?.offsetTop??0,i=t+(e?.height??window.innerHeight);for(let o=this.composedParent(this);o;o=this.composedParent(o)){const s=getComputedStyle(o).overflowY;if(s!=="auto"&&s!=="scroll")continue;const a=o.getBoundingClientRect(),r=o instanceof HTMLElement?o.clientTop:0,u=o instanceof HTMLElement?o.clientHeight:a.height;if(u<=0)continue;const c=a.top+r;t=Math.max(t,c),i=Math.min(i,c+u)}return{top:t,bottom:Math.max(t,i)}}composedParent(e){if(e.parentElement)return e.parentElement;const t=e.getRootNode();return t instanceof ShadowRoot?t.host:null}setGeometryProperty(e,t){this.style.getPropertyValue(e)!==t&&this.style.setProperty(e,t)}clearAnchorGeometry(){this.style.removeProperty("--_esp-flyout-anchor-offset"),this.style.removeProperty("--_esp-flyout-viewport-shift"),this.style.removeProperty("--_esp-flyout-max-block-size"),this.style.removeProperty("--_esp-flyout-anchor-block-size"),this.style.removeProperty("--_esp-flyout-anchor-marker-offset")}scheduleAnchorGeometrySync(){!this.isConnected||this.anchorPositionRaf||(this.anchorPositionRaf=requestAnimationFrame(()=>{this.anchorPositionRaf=0,this.syncAnchorGeometry()}))}syncAnchorTracking(){if(!(this.isConnected&&this.open&&!!this.anchor&&!this.effectiveOverlay)){this.stopAnchorTracking();return}this.trackingAnchorPosition||(this.trackingAnchorPosition=!0,window.addEventListener("scroll",this.boundAnchorViewportChange,{capture:!0,passive:!0}),window.addEventListener("resize",this.boundAnchorViewportChange,{passive:!0}));const t=this.panelRef.value;t&&t!==this.observedAnchorPanel&&typeof ResizeObserver<"u"&&(this.anchorResizeObserver??=new ResizeObserver(this.boundAnchorViewportChange),this.anchorResizeObserver.disconnect(),this.anchorResizeObserver.observe(t),this.observedAnchorPanel=t)}stopAnchorTracking(){this.trackingAnchorPosition&&(window.removeEventListener("scroll",this.boundAnchorViewportChange,{capture:!0}),window.removeEventListener("resize",this.boundAnchorViewportChange),this.trackingAnchorPosition=!1),this.anchorPositionRaf&&(cancelAnimationFrame(this.anchorPositionRaf),this.anchorPositionRaf=0),this.anchorResizeObserver?.disconnect(),this.observedAnchorPanel=void 0}syncOverlayModal(){const e=this.isConnected&&this.open&&this.effectiveOverlay;if(e===this.overlayActive){!e&&!this.open&&this.overlay.close();return}e?(this.overlayActive=!0,this.setAttribute("role","dialog"),this.setAttribute("aria-modal","true"),this.updateGeneratedAriaLabel(),this.overlay.open(),this.updateComplete.then(()=>{this.overlayActive&&this.overlay.moveFocusInto()})):(this.overlayActive=!1,this.overlay.close(),this.overlayReturnFocusTo&&(this.overlayReturnFocusTo.focus(),this.overlayReturnFocusTo=void 0),this.setAttribute("role",this.baseRole),this.removeAttribute("aria-modal"),this.updateGeneratedAriaLabel())}updateGeneratedAriaLabel(){const e=this.getAttribute("aria-label");if(e!==null&&e!==this.lastGeneratedAriaLabel)return;const t=this.heading||(this.overlayActive?q:"");t?(this.setAttribute("aria-label",t),this.lastGeneratedAriaLabel=t):(this.removeAttribute("aria-label"),this.lastGeneratedAriaLabel=null)}render(){return w`
34
+ `;let a=class extends k{constructor(){super(...arguments),this.open=!1,this.heading="",this.mode="auto",this.pageOverlayRequested=!1,this.fullHeight=!1,this.matchSurface=!1,this.standalone=!1,this.scope="outermost",this.lastGeneratedAriaLabel=null,this.boundKeydown=e=>this.handleKeydown(e),this.boundShowRequest=e=>this.handleShowRequest(e),this.boundCloseRequest=()=>this.close(),this.boundOverlayMediaChange=()=>{this.syncOverlayModal(),this.syncAnchorTracking(),this.syncAnchorGeometry(),this.dispatchEvent(new CustomEvent("flyout-state-changed",{detail:{},bubbles:!0,composed:!0}))},this.boundAnchorViewportChange=()=>this.scheduleAnchorGeometrySync(),this.boundFullHeightRequest=e=>{e.stopPropagation(),this.fullHeight=!0},this.anchorPositionRaf=0,this.trackingAnchorPosition=!1,this.busSubscribed=!1,this.panelRef=F(),this.overlay=new q({host:this,getFocusTrapContainer:()=>this.panelRef.value??null,getFocusFallback:()=>this.panelRef.value??null,promote:!1}),this.overlayActive=!1,this.baseRole="complementary"}get effectiveOverlay(){return this.mode==="overlay"||this.pageOverlayRequested||(this.overlayMediaQuery?.matches??!1)}connectedCallback(){super.connectedCallback(),this.hasAttribute("role")||this.setAttribute("role","complementary"),this.baseRole=this.getAttribute("role")??"complementary",!this.overlayMediaQuery&&typeof window<"u"&&"matchMedia"in window&&(this.overlayMediaQuery=window.matchMedia(B)),this.overlayMediaQuery?.addEventListener("change",this.boundOverlayMediaChange),this.syncBusSubscription(),document.addEventListener("keydown",this.boundKeydown),this.addEventListener(A,this.boundFullHeightRequest),this.syncOverlayModal(),this.syncAnchorTracking(),this.syncAnchorGeometry()}disconnectedCallback(){super.disconnectedCallback(),this.unsubscribeBus(),this.overlayMediaQuery?.removeEventListener("change",this.boundOverlayMediaChange),document.removeEventListener("keydown",this.boundKeydown),this.removeEventListener(A,this.boundFullHeightRequest),this.stopAnchorTracking(),this.overlayActive&&(this.overlayActive=!1,this.setAttribute("role",this.baseRole),this.removeAttribute("aria-modal"),this.updateGeneratedAriaLabel()),this.overlayReturnFocusTo=void 0}syncBusSubscription(){if(this.standalone||!this.isConnected){this.unsubscribeBus();return}this.busSubscribed||(f().subscribe("show-flyout",this.boundShowRequest),f().subscribe("close-flyout",this.boundCloseRequest),this.busSubscribed=!0)}unsubscribeBus(){this.busSubscribed&&(f().unsubscribe("show-flyout",this.boundShowRequest),f().unsubscribe("close-flyout",this.boundCloseRequest),this.busSubscribed=!1)}show(){this.open||(this.open=!0,this.dispatchEvent(new CustomEvent("flyout-opened",{detail:{},bubbles:!0,composed:!0})))}close(e="programmatic"){if(this.open){if(this.open=!1,this.overlayActive)this.overlayReturnFocusTo=this.returnFocusTo;else{const t=this.focusIsInside();this.overlay.close(),t&&this.returnFocusTo?.focus()}this.returnFocusTo=void 0,this.dispatchEvent(new CustomEvent("flyout-closed",{detail:{reason:e},bubbles:!0,composed:!0}))}}toggle(){this.open?this.close():this.show()}handleShowRequest(e){this.shouldHandleShowRequest(e)&&(M(e),e.heading!==void 0&&(this.heading=e.heading),e.content!==void 0&&this.replaceChildren(typeof e.content=="string"?document.createTextNode(e.content):e.content),this.anchor=e.anchor,this.fullHeight=e.fullHeight??!1,this.returnFocusTo=e.returnFocusTo,this.show())}shouldHandleShowRequest(e){const t=v(this,"esp-page"),r=e.anchor??e.returnFocusTo;if(!r){if(!t)return!0;const i=this.getPageChain(t);return this.scope==="outermost"&&i[i.length-1]===t}const o=this.getPageChain(r);if(o.length===0)return t===null;const s=o.find(i=>this.hasNearestScopeFlyout(i)),n=s??o[o.length-1];return t!==n?!1:s?this.scope==="nearest":this.scope==="outermost"}getPageChain(e){const t=[];let r=e;for(;r;){const o=v(r,"esp-page");if(!o||t.includes(o))break;t.push(o),r=this.composedParent(o)}return t}hasNearestScopeFlyout(e){return Array.from(e.querySelectorAll("esp-flyout")).some(t=>!t.standalone&&t.scope==="nearest"&&v(t,"esp-page")===e)}handleKeydown(e){if(this.open){if(this.overlayActive&&e.key==="Tab"){this.overlay.trapFocus(e);return}e.key!=="Escape"||e.defaultPrevented||(e.preventDefault(),this.close("escape"))}}focusIsInside(){const e=document.activeElement;return e&&this.contains(e)?!0:this.shadowRoot?.activeElement!=null}updated(e){super.updated(e),e.has("standalone")&&this.syncBusSubscription(),e.has("heading")&&this.updateGeneratedAriaLabel(),(e.has("anchor")||e.has("fullHeight")||e.has("open")||e.has("mode")||e.has("pageOverlayRequested"))&&(this.syncAnchorTracking(),this.syncAnchorGeometry()),(e.has("open")||e.has("mode")||e.has("pageOverlayRequested"))&&this.syncOverlayModal(),(e.has("open")||e.has("mode")||e.has("anchor")||e.has("pageOverlayRequested"))&&this.dispatchEvent(new CustomEvent("flyout-state-changed",{detail:{},bubbles:!0,composed:!0}))}syncAnchorGeometry(){const e=v(this,"esp-page"),t=this.anchor?this.getPageChain(this.anchor):[],r=e?.shadowRoot?.querySelector(".esp-page-main"),o=this.panelRef.value;if(!this.open||this.effectiveOverlay||!this.anchor||!e||!t.includes(e)||!r||!o){this.clearAnchorGeometry();return}const s=this.anchor.getBoundingClientRect(),n=Math.max(0,s.top-r.getBoundingClientRect().top),i=this.getVisibleBlockBounds(),u=Math.max(0,i.bottom-i.top),c=Number.isFinite(s.height)?s.height:0;this.setGeometryProperty("--_esp-flyout-anchor-offset",`${n}px`),this.setGeometryProperty("--_esp-flyout-max-block-size",`${u}px`),this.setGeometryProperty("--_esp-flyout-anchor-block-size",`${c}px`);const y=o.getBoundingClientRect().height||u,b=Math.max(0,s.top+y-i.bottom),P=Math.max(0,y-c),g=this.fullHeight?b:Math.min(b,P);this.setGeometryProperty("--_esp-flyout-viewport-shift",`${g}px`);const C=s.top-g,m=8,E=s.top+c/2,S=Math.min(Math.max(m,y-m),Math.max(m,E-C));this.setGeometryProperty("--_esp-flyout-anchor-marker-offset",`${S}px`)}getVisibleBlockBounds(){const e=window.visualViewport;let t=e?.offsetTop??0,r=t+(e?.height??window.innerHeight);for(let o=this.composedParent(this);o;o=this.composedParent(o)){const s=getComputedStyle(o).overflowY;if(s!=="auto"&&s!=="scroll")continue;const n=o.getBoundingClientRect(),i=o instanceof HTMLElement?o.clientTop:0,u=o instanceof HTMLElement?o.clientHeight:n.height;if(u<=0)continue;const c=n.top+i;t=Math.max(t,c),r=Math.min(r,c+u)}return{top:t,bottom:Math.max(t,r)}}composedParent(e){if(e.parentElement)return e.parentElement;const t=e.getRootNode();return t instanceof ShadowRoot?t.host:null}setGeometryProperty(e,t){this.style.getPropertyValue(e)!==t&&this.style.setProperty(e,t)}clearAnchorGeometry(){this.style.removeProperty("--_esp-flyout-anchor-offset"),this.style.removeProperty("--_esp-flyout-viewport-shift"),this.style.removeProperty("--_esp-flyout-max-block-size"),this.style.removeProperty("--_esp-flyout-anchor-block-size"),this.style.removeProperty("--_esp-flyout-anchor-marker-offset")}scheduleAnchorGeometrySync(){!this.isConnected||this.anchorPositionRaf||(this.anchorPositionRaf=requestAnimationFrame(()=>{this.anchorPositionRaf=0,this.syncAnchorGeometry()}))}syncAnchorTracking(){if(!(this.isConnected&&this.open&&!!this.anchor&&!this.effectiveOverlay)){this.stopAnchorTracking();return}this.trackingAnchorPosition||(this.trackingAnchorPosition=!0,window.addEventListener("scroll",this.boundAnchorViewportChange,{capture:!0,passive:!0}),window.addEventListener("resize",this.boundAnchorViewportChange,{passive:!0}));const t=this.panelRef.value;t&&t!==this.observedAnchorPanel&&typeof ResizeObserver<"u"&&(this.anchorResizeObserver??=new ResizeObserver(this.boundAnchorViewportChange),this.anchorResizeObserver.disconnect(),this.anchorResizeObserver.observe(t),this.observedAnchorPanel=t)}stopAnchorTracking(){this.trackingAnchorPosition&&(window.removeEventListener("scroll",this.boundAnchorViewportChange,{capture:!0}),window.removeEventListener("resize",this.boundAnchorViewportChange),this.trackingAnchorPosition=!1),this.anchorPositionRaf&&(cancelAnimationFrame(this.anchorPositionRaf),this.anchorPositionRaf=0),this.anchorResizeObserver?.disconnect(),this.observedAnchorPanel=void 0}syncOverlayModal(){const e=this.isConnected&&this.open&&this.effectiveOverlay;if(e===this.overlayActive){!e&&!this.open&&this.overlay.close();return}e?(this.overlayActive=!0,this.setAttribute("role","dialog"),this.setAttribute("aria-modal","true"),this.updateGeneratedAriaLabel(),this.overlay.open(),this.updateComplete.then(()=>{this.overlayActive&&this.overlay.moveFocusInto()})):(this.overlayActive=!1,this.overlay.close(),this.overlayReturnFocusTo&&(this.overlayReturnFocusTo.focus(),this.overlayReturnFocusTo=void 0),this.setAttribute("role",this.baseRole),this.removeAttribute("aria-modal"),this.updateGeneratedAriaLabel())}updateGeneratedAriaLabel(){const e=this.getAttribute("aria-label");if(e!==null&&e!==this.lastGeneratedAriaLabel)return;const t=this.heading||(this.overlayActive?H:"");t?(this.setAttribute("aria-label",t),this.lastGeneratedAriaLabel=t):(this.removeAttribute("aria-label"),this.lastGeneratedAriaLabel=null)}render(){return w`
35
35
  <div class="vellum" aria-hidden="true" @click=${()=>this.close("vellum")}></div>
36
36
  <div
37
- class=${L({panel:!0,anchored:!!this.anchor,"full-height":this.fullHeight})}
37
+ class=${_({panel:!0,anchored:!!this.anchor,"full-height":this.fullHeight})}
38
38
  part="panel"
39
- ${O(this.panelRef)}
39
+ ${L(this.panelRef)}
40
40
  >
41
+ <span class="anchor-terminus" aria-hidden="true"></span>
41
42
  <header part="header">
42
- ${this.heading?w`<h2>${this.heading}</h2>`:S}
43
+ ${this.heading?w`<h2>${this.heading}</h2>`:T}
43
44
 
44
45
  <button class="close" aria-label="Close" @click=${()=>this.close("button")}>
45
- ${B}
46
+ ${G}
46
47
  </button>
47
48
  </header>
48
49
  <div class="content" part="content">
49
50
  <slot @slotchange=${this.boundAnchorViewportChange}></slot>
50
51
  </div>
51
52
  </div>
52
- `}};n.styles=[...A.styles,p`
53
+ `}};a.styles=[...k.styles,p`
53
54
  :host {
54
55
  display: none;
55
56
  }
@@ -57,7 +58,10 @@ var h=function(d,e,t,i){var o=arguments.length,s=o<3?e:i===null?i=Object.getOwnP
57
58
  :host([open]) {
58
59
  display: block;
59
60
 
60
- width: var(--esp-page-flyout-width, 20rem);
61
+ width: var(
62
+ --_esp-flyout-used-width,
63
+ var(--esp-page-flyout-width, var(--esp-page-flyout-min-width, 20rem))
64
+ );
61
65
  margin-block-start: calc(
62
66
  var(--_esp-flyout-anchor-offset, 0px) - var(--_esp-flyout-viewport-shift, 0px)
63
67
  );
@@ -70,7 +74,10 @@ var h=function(d,e,t,i){var o=arguments.length,s=o<3?e:i===null?i=Object.getOwnP
70
74
  .panel {
71
75
  background: var(--esp-flyout-background, var(--esp-color-background));
72
76
 
73
- width: var(--esp-page-flyout-width, 20rem);
77
+ width: var(
78
+ --_esp-flyout-used-width,
79
+ var(--esp-page-flyout-width, var(--esp-page-flyout-min-width, 20rem))
80
+ );
74
81
  display: flex;
75
82
  flex-direction: column;
76
83
  position: relative;
@@ -96,17 +103,34 @@ var h=function(d,e,t,i){var o=arguments.length,s=o<3?e:i===null?i=Object.getOwnP
96
103
  &.full-height {
97
104
  block-size: var(--_esp-flyout-max-block-size, 100dvh);
98
105
 
99
- &::before {
106
+ &::before,
107
+ &::after,
108
+ > .anchor-terminus {
100
109
  display: none;
101
110
  }
102
111
  }
103
112
 
113
+ > .anchor-terminus {
114
+ display: block;
115
+ position: absolute;
116
+ z-index: 2;
117
+ inset-block-start: var(--_esp-flyout-anchor-marker-offset, 50%);
118
+ inset-inline-start: -2px;
119
+ inline-size: 4px;
120
+ block-size: var(--esp-size-medium);
121
+ translate: 0 -50%;
122
+ background: var(--esp-color-border);
123
+ pointer-events: none;
124
+ }
125
+
104
126
  &::before {
105
127
  content: "";
106
128
  position: absolute;
107
129
  z-index: 1;
108
130
  inset-block-start: var(--_esp-flyout-anchor-marker-offset, 50%);
109
- inset-inline-start: calc(-1 * var(--esp-size-small));
131
+ inset-inline-start: calc(
132
+ -1 * (var(--esp-size-small) + var(--_esp-flyout-preview-bridge-width, 0px))
133
+ );
110
134
  inline-size: var(--esp-size-small);
111
135
  block-size: var(--esp-size-medium);
112
136
  translate: 0 -50%;
@@ -114,6 +138,24 @@ var h=function(d,e,t,i){var o=arguments.length,s=o<3?e:i===null?i=Object.getOwnP
114
138
  background: var(--esp-color-border);
115
139
  pointer-events: none;
116
140
  }
141
+
142
+
143
+ &::after {
144
+ content: "";
145
+ position: absolute;
146
+ z-index: 1;
147
+ inset-block-start: var(--_esp-flyout-anchor-marker-offset, 50%);
148
+ inset-inline-start: calc(-1 * var(--_esp-flyout-preview-bridge-width, 0px));
149
+ inline-size: var(--_esp-flyout-preview-bridge-width, 0px);
150
+ border-block-start: 2px dotted var(--esp-color-border);
151
+ opacity: 0.4;
152
+ translate: 0 -50%;
153
+ pointer-events: none;
154
+ }
155
+ }
156
+
157
+ > .anchor-terminus {
158
+ display: none;
117
159
  }
118
160
 
119
161
  > header {
@@ -152,25 +194,34 @@ var h=function(d,e,t,i){var o=arguments.length,s=o<3?e:i===null?i=Object.getOwnP
152
194
  flex: 1;
153
195
  min-block-size: 0;
154
196
  overflow-y: auto;
155
- overscroll-behavior-block: contain;
197
+
198
+ overscroll-behavior-block: auto;
156
199
  padding: var(--esp-flyout-padding, var(--esp-size-padding));
157
200
  }
158
201
  }
159
202
 
160
203
 
161
- :host([open][mode="overlay"]) {
204
+ :host([open][mode="overlay"]),
205
+ :host([open][data-page-overlay]) {
162
206
  ${R}
163
207
  }
164
208
 
165
- :host([open][mode="overlay"]) .vellum {
209
+ :host([open][mode="overlay"]) .vellum,
210
+ :host([open][data-page-overlay]) .vellum {
166
211
  ${x}
167
212
  }
168
213
 
169
- :host([open][mode="overlay"]) .panel {
170
- ${P}
214
+ :host([open][mode="overlay"]) .panel,
215
+ :host([open][data-page-overlay]) .panel {
216
+ ${z}
171
217
  }
172
218
 
173
- :host([open][mode="overlay"]) .panel.anchored::before {
219
+ :host([open][mode="overlay"]) .panel.anchored::before,
220
+ :host([open][mode="overlay"]) .panel.anchored::after,
221
+ :host([open][mode="overlay"]) .panel.anchored > .anchor-terminus,
222
+ :host([open][data-page-overlay]) .panel.anchored::before,
223
+ :host([open][data-page-overlay]) .panel.anchored::after,
224
+ :host([open][data-page-overlay]) .panel.anchored > .anchor-terminus {
174
225
  display: none;
175
226
  }
176
227
 
@@ -185,10 +236,12 @@ var h=function(d,e,t,i){var o=arguments.length,s=o<3?e:i===null?i=Object.getOwnP
185
236
  }
186
237
 
187
238
  .panel {
188
- ${P}
239
+ ${z}
189
240
  }
190
241
 
191
- .panel.anchored::before {
242
+ .panel.anchored::before,
243
+ .panel.anchored::after,
244
+ .panel.anchored > .anchor-terminus {
192
245
  display: none;
193
246
  }
194
247
  }
@@ -198,4 +251,4 @@ var h=function(d,e,t,i){var o=arguments.length,s=o<3?e:i===null?i=Object.getOwnP
198
251
  transition: none;
199
252
  }
200
253
  }
201
- `],h([l({type:Boolean,reflect:!0})],n.prototype,"open",void 0),h([l({type:String})],n.prototype,"heading",void 0),h([l({reflect:!0})],n.prototype,"mode",void 0),h([l({attribute:!1})],n.prototype,"anchor",void 0),h([l({type:Boolean,reflect:!0,attribute:"full-height"})],n.prototype,"fullHeight",void 0),h([l({type:Boolean,reflect:!0,attribute:"match-surface"})],n.prototype,"matchSurface",void 0),h([l({type:Boolean,reflect:!0})],n.prototype,"standalone",void 0),h([l({type:String,reflect:!0})],n.prototype,"scope",void 0),n=h([F("esp-flyout")],n);export{n as EspalierFlyout};
254
+ `],l([h({type:Boolean,reflect:!0})],a.prototype,"open",void 0),l([h({type:String})],a.prototype,"heading",void 0),l([h({reflect:!0})],a.prototype,"mode",void 0),l([h({type:Boolean,reflect:!0,attribute:"data-page-overlay"})],a.prototype,"pageOverlayRequested",void 0),l([h({attribute:!1})],a.prototype,"anchor",void 0),l([h({type:Boolean,reflect:!0,attribute:"full-height"})],a.prototype,"fullHeight",void 0),l([h({type:Boolean,reflect:!0,attribute:"match-surface"})],a.prototype,"matchSurface",void 0),l([h({type:Boolean,reflect:!0})],a.prototype,"standalone",void 0),l([h({type:String,reflect:!0})],a.prototype,"scope",void 0),a=l([O("esp-flyout")],a);export{a as EspalierFlyout};
@@ -1 +1 @@
1
- import{getEspBus as d}from"../shared/bus-events.js";class f{getEffectiveTopOffset(){if(this.isHeaderFixed)return this.headerHeight;if(!this.headerElement)return 0;const s=this.headerElement.getBoundingClientRect().bottom;return Math.max(0,Math.min(s,this.headerHeight))}constructor(s,c){this.lastScrollY=0,this.mode="natural",this.headerHeight=0,this.isHeaderFixed=!1,this.frozenDocumentTop=0,this.scrollDirection=null,this.headerElement=null,this._pendingSetup=null,this.bus=d(),this.onFixedMenusChanged=({fixed:e})=>{this.isHeaderFixed=e,this.detectHeaderState(),this.mode="natural",this.scrollDirection=null;const t=this.host.shadowRoot?.querySelector(this.targetSelector);t&&(t.style.top=`${this.getEffectiveTopOffset()}px`),this.lastScrollY=window.scrollY},this.handleResize=()=>{this.detectHeaderState(),this.mode="natural",this.scrollDirection=null;const e=this.host.shadowRoot?.querySelector(this.targetSelector);e&&(e.style.top=`${this.getEffectiveTopOffset()}px`),this.lastScrollY=window.scrollY},this.handleScroll=()=>{const e=this.host.shadowRoot?.querySelector(this.targetSelector);if(!e)return;const t=window.scrollY,l=window.innerHeight,h=e.offsetHeight;if(t===this.lastScrollY)return;const a=t>this.lastScrollY,o=this.getEffectiveTopOffset();if(h+o<=l){e.style.top=`${o}px`,this.mode="natural",this.lastScrollY=t;return}const i=e.getBoundingClientRect(),n=l-h;if(a)switch(this.mode){case"natural":e.style.top=`${n}px`,this.mode="stuck-bottom";break;case"stuck-top":this.frozenDocumentTop=i.top+t,this.scrollDirection="down",this.mode="scrolling",e.style.top=`${i.top}px`;break;case"scrolling":if(this.scrollDirection==="down"){const r=this.frozenDocumentTop-t;r<=n?(e.style.top=`${n}px`,this.mode="stuck-bottom"):e.style.top=`${r}px`}else this.frozenDocumentTop=i.top+t,this.scrollDirection="down",e.style.top=`${i.top}px`;break;case"stuck-bottom":break}else switch(this.mode){case"natural":e.style.top=`${o}px`,this.mode="stuck-top";break;case"stuck-bottom":this.frozenDocumentTop=i.top+t,this.scrollDirection="up",this.mode="scrolling",e.style.top=`${i.top}px`;break;case"scrolling":if(this.scrollDirection==="up"){const r=this.frozenDocumentTop-t;r>=o?(e.style.top=`${o}px`,this.mode="stuck-top"):e.style.top=`${r}px`}else this.frozenDocumentTop=i.top+t,this.scrollDirection="up",e.style.top=`${i.top}px`;break;case"stuck-top":this.isHeaderFixed||(e.style.top=`${o}px`);break}this.lastScrollY=t},(this.host=s).addController(this),this.targetSelector=c,this.resizeObserver=new ResizeObserver(()=>this.handleResize())}hostConnected(){window.addEventListener("scroll",this.handleScroll,{passive:!0}),window.addEventListener("resize",this.handleResize),this.bus.subscribe("fixed-menus-changed",this.onFixedMenusChanged),this.setupTargetObservation()}hostDisconnected(){window.removeEventListener("scroll",this.handleScroll),window.removeEventListener("resize",this.handleResize),this.bus.unsubscribe("fixed-menus-changed",this.onFixedMenusChanged),this.resizeObserver.disconnect(),this._pendingSetup!==null&&(clearTimeout(this._pendingSetup),this._pendingSetup=null)}setupTargetObservation(){if(this._pendingSetup=null,!this.host.isConnected)return;const s=this.host.shadowRoot?.querySelector(this.targetSelector);s?(this.resizeObserver.observe(s),this.detectHeaderState(),this.lastScrollY=window.scrollY,s.style.top=`${this.getEffectiveTopOffset()}px`):this._pendingSetup=setTimeout(()=>this.setupTargetObservation(),100)}detectHeaderState(){this.headerElement=this.host.shadowRoot?.querySelector(".esp-page-top")??null,this.headerElement&&(this.headerHeight=this.headerElement.offsetHeight);const s=this.host.shadowRoot?.querySelector(".esp-page");this.isHeaderFixed=s?.classList.contains("fixed-header")||s?.classList.contains("fixed-menus")||!1}}export{f as BiDirectionalStickyController};
1
+ import{getEspBus as p}from"../shared/bus-events.js";class g{getEffectiveTopOffset(){const s=this.topOffsetOverride?.();if(s!==void 0)return s;if(this.isHeaderFixed)return this.headerHeight;if(!this.headerElement)return 0;const n=this.headerElement.getBoundingClientRect().bottom;return Math.max(0,Math.min(n,this.headerHeight))}constructor(s,n,a={}){this.lastScrollY=0,this.mode="natural",this.headerHeight=0,this.isHeaderFixed=!1,this.frozenDocumentTop=0,this.scrollDirection=null,this.headerElement=null,this._pendingSetup=null,this.bus=p(),this.onFixedMenusChanged=({fixed:e})=>{this.isHeaderFixed=e,this.detectHeaderState(),this.mode="natural",this.scrollDirection=null;const t=this.host.shadowRoot?.querySelector(this.targetSelector);t&&(t.style.top=`${this.getEffectiveTopOffset()}px`),this.lastScrollY=window.scrollY},this.handleResize=()=>{this.detectHeaderState(),this.mode="natural",this.scrollDirection=null;const e=this.host.shadowRoot?.querySelector(this.targetSelector);e&&(e.style.top=`${this.getEffectiveTopOffset()}px`),this.lastScrollY=window.scrollY},this.handleScroll=()=>{const e=this.host.shadowRoot?.querySelector(this.targetSelector);if(!e)return;const t=window.scrollY,h=window.innerHeight,c=e.offsetHeight;if(t===this.lastScrollY)return;const d=t>this.lastScrollY,o=this.getEffectiveTopOffset();if(c+o<=h){e.style.top=`${o}px`,this.mode="natural",this.lastScrollY=t;return}const i=e.getBoundingClientRect(),l=h-c;if(d)switch(this.mode){case"natural":e.style.top=`${l}px`,this.mode="stuck-bottom";break;case"stuck-top":this.frozenDocumentTop=i.top+t,this.scrollDirection="down",this.mode="scrolling",e.style.top=`${i.top}px`;break;case"scrolling":if(this.scrollDirection==="down"){const r=this.frozenDocumentTop-t;r<=l?(e.style.top=`${l}px`,this.mode="stuck-bottom"):e.style.top=`${r}px`}else this.frozenDocumentTop=i.top+t,this.scrollDirection="down",e.style.top=`${i.top}px`;break;case"stuck-bottom":break}else switch(this.mode){case"natural":e.style.top=`${o}px`,this.mode="stuck-top";break;case"stuck-bottom":this.frozenDocumentTop=i.top+t,this.scrollDirection="up",this.mode="scrolling",e.style.top=`${i.top}px`;break;case"scrolling":if(this.scrollDirection==="up"){const r=this.frozenDocumentTop-t;r>=o?(e.style.top=`${o}px`,this.mode="stuck-top"):e.style.top=`${r}px`}else this.frozenDocumentTop=i.top+t,this.scrollDirection="up",e.style.top=`${i.top}px`;break;case"stuck-top":this.isHeaderFixed||(e.style.top=`${o}px`);break}this.lastScrollY=t},(this.host=s).addController(this),this.targetSelector=n,this.topOffsetOverride=a.topOffset,this.resizeObserver=new ResizeObserver(()=>this.handleResize())}hostConnected(){window.addEventListener("scroll",this.handleScroll,{passive:!0}),window.addEventListener("resize",this.handleResize),this.bus.subscribe("fixed-menus-changed",this.onFixedMenusChanged),this.setupTargetObservation()}hostDisconnected(){window.removeEventListener("scroll",this.handleScroll),window.removeEventListener("resize",this.handleResize),this.bus.unsubscribe("fixed-menus-changed",this.onFixedMenusChanged),this.resizeObserver.disconnect(),this._pendingSetup!==null&&(clearTimeout(this._pendingSetup),this._pendingSetup=null)}setupTargetObservation(){if(this._pendingSetup=null,!this.host.isConnected)return;const s=this.host.shadowRoot?.querySelector(this.targetSelector);s?(this.resizeObserver.observe(s),this.detectHeaderState(),this.lastScrollY=window.scrollY,s.style.top=`${this.getEffectiveTopOffset()}px`):this._pendingSetup=setTimeout(()=>this.setupTargetObservation(),100)}detectHeaderState(){this.headerElement=this.host.shadowRoot?.querySelector(".esp-page-top")??null,this.headerElement&&(this.headerHeight=this.headerElement.offsetHeight);const s=this.host.shadowRoot?.querySelector(".esp-page");this.isHeaderFixed=s?.classList.contains("fixed-header")||s?.classList.contains("fixed-menus")||!1}}export{g as BiDirectionalStickyController};
@@ -32,38 +32,30 @@ type HeaderPosition = "normal" | "sticky" | "fixed";
32
32
  * </esp-page>
33
33
  * ```
34
34
  *
35
- * A persistent preview uses spare trailing canvas first. Set a main-well
36
- * floor to let it reclaim surface width, and opt into collapsing a directly
37
- * paired header/sidebar navigation to its accessible burger/drawer mode:
38
- *
39
- * ```html
40
- * <esp-page
41
- * preview-open
42
- * preview-collapse-sidebar
43
- * style="--esp-page-preview-width: 20rem; --esp-page-preview-min-main-width: 900px;"
44
- * >
45
- * <esp-header slot="header" drawer-target="site-nav"></esp-header>
46
- * <esp-menu id="site-nav" slot="sidebar" mode="vertical"></esp-menu>
47
- * <article>Primary content</article>
48
- * <aside slot="preview">Preview content</aside>
49
- * </esp-page>
50
- * ```
35
+ * The authoring workspace is ordered main, preview, then help. Each region
36
+ * negotiates between public minimum and maximum widths; preview grows first,
37
+ * then help. Opt into collapsing a directly paired header/sidebar navigation
38
+ * to its accessible burger/drawer mode when the combined workspace needs it.
51
39
  *
52
40
  * @slot sidebar - Contextual navigation placed in the left aside.
53
41
  * @slot right - Content to place in the right aside.
54
- * @slot preview - Persistent preview content that uses spare right canvas
55
- * width first, then optionally reclaims navigation and main-well width down
56
- * to `--esp-page-preview-min-main-width`. Unlike flyout content, preview
57
- * content is never replaced by the flyout request bus.
42
+ * @slot preview - Persistent preview content placed after the main surface and
43
+ * before help. It grows from `--esp-page-preview-min-width` to
44
+ * `--esp-page-preview-max-width`, starts at viewport top, and gives its content
45
+ * an independent `100dvh` vertical scroller. At either scroll boundary, wheel
46
+ * and trackpad motion chains back to the main document. Unlike flyout content,
47
+ * preview content is never replaced by the flyout request bus.
58
48
  * @slot flyout - A transient `esp-flyout` panel that lives on the
59
- * canvas, outside the content surface. Closed it costs no width; open
60
- * it claims the right canvas gutter first — the surface keeps its
61
- * alignment weighting and shifts only as far as the flyout's width
62
- * requires then docks as a width-competing right sidebar when no
63
- * gutter exists, and becomes an overlay drawer on small viewports.
49
+ * canvas after preview. Closed it costs no width; open it grows from
50
+ * `--esp-page-flyout-min-width` to `--esp-page-flyout-max-width` after preview
51
+ * receives flexible space, then docks as a width-competing right sidebar when
52
+ * no gutter exists, and becomes an overlay drawer on small viewports.
64
53
  * The persistent `right` aside and the transient flyout are
65
54
  * complementary, not alternatives. A flyout opened with an `anchor`
66
- * aligns to that trigger and stays in the same document scroll flow.
55
+ * aligns to that trigger and stays in the same document scroll flow. When a
56
+ * preview is visible, its caret and non-interactive dotted bridge cross the
57
+ * preview back to the anchor, ending at a short thick marker on the
58
+ * preview/help seam.
67
59
  * @slot footer - Content to place in the footer. Slot `esp-footer` directly
68
60
  * to receive the page's surface-alignment custom-property contract. A wrapper
69
61
  * can preserve landmark semantics but does not receive that direct contract.
@@ -131,10 +123,13 @@ type HeaderPosition = "normal" | "sticky" | "fixed";
131
123
  * @cssprop --esp-page-background - The background color of the page. Fills
132
124
  * both the surface and the canvas gutters; the canvas tokens paint over it
133
125
  * in the gutters only. Defaults to `var(--esp-color-background)`.
134
- * @cssprop --esp-page-max-width - The maximum width of the main content
135
- * well. Set automatically by the `kind` attribute: `1536px` (wide),
136
- * `768px` (narrow), or `none` (full). This cap now sizes the main grid
137
- * track; surplus width beyond it becomes the canvas gutters.
126
+ * @cssprop --esp-page-main-min-width - Minimum width of the main content well
127
+ * while preview or in-grid help competes for space. Defaults to `30rem`.
128
+ * @cssprop --esp-page-main-max-width - Maximum width of the main content well.
129
+ * Defaults from `kind`: `1536px` (wide), `768px` (narrow), or unbounded (full).
130
+ * Surplus width beyond it becomes canvas gutters.
131
+ * @cssprop --esp-page-max-width - Legacy fallback for
132
+ * `--esp-page-main-max-width`.
138
133
  * @cssprop --esp-page-background-image - The background image to
139
134
  * display behind page content. Defaults to `none`.
140
135
  * @cssprop --esp-page-background-image-opacity - The opacity of the
@@ -162,14 +157,27 @@ type HeaderPosition = "normal" | "sticky" | "fixed";
162
157
  * background). Set it to give the content well its own card color, or
163
158
  * pair the transparent default with `--esp-page-surface-shadow: none`
164
159
  * for content that floats directly on the page with no frame.
165
- * @cssprop --esp-page-flyout-width - The width of the open flyout
166
- * track (and of the `esp-flyout` overlay drawer). Defaults to `20rem`.
167
- * @cssprop --esp-page-preview-width - Width of the persistent preview
168
- * panel. Defaults to `20rem`.
169
- * @cssprop --esp-page-preview-min-main-width - Optional minimum width of
170
- * the main content well while preview is reclaiming canvas/surface space.
171
- * Unset by default, so preview uses spare canvas only. Set a length such
172
- * as `900px` to opt into reclaim behavior.
160
+ * @cssprop --esp-page-preview-background - Background of the persistent
161
+ * preview surface. Defaults to the main background, then the page background.
162
+ * @cssprop --esp-page-preview-border - Leading tear-off edge between main and
163
+ * preview. Defaults to `1px dotted var(--esp-color-border)`.
164
+ * @cssprop --esp-page-preview-shadow - Shadow cast from the preview's trailing
165
+ * edge. Defaults to the page surface edge shadow. Set to `none` for a flat
166
+ * additive surface.
167
+ * @cssprop --esp-page-preview-min-width - Minimum preview width. Defaults to
168
+ * `22.5rem` (360px, a reasonable mobile-device surface).
169
+ * @cssprop --esp-page-preview-max-width - Maximum preview width. Defaults to
170
+ * `48rem`. Preview receives flexible trailing space before help grows.
171
+ * @cssprop --esp-page-flyout-min-width - Minimum in-grid help width. Defaults
172
+ * to `20rem`.
173
+ * @cssprop --esp-page-flyout-max-width - Maximum help width and overlay drawer
174
+ * cap before the `85vw` viewport cap. Defaults to `30rem`.
175
+ * @cssprop --esp-page-preview-width - Legacy fixed-width alias that pins both
176
+ * preview bounds.
177
+ * @cssprop --esp-page-flyout-width - Legacy fixed-width alias that pins both
178
+ * help bounds.
179
+ * @cssprop --esp-page-preview-min-main-width - Legacy fallback for
180
+ * `--esp-page-main-min-width`.
173
181
  * @cssprop --esp-page-fixed-header-offset - Offset reserved for fixed
174
182
  * headers. Defaults to `var(--esp-header-height)`.
175
183
  * @cssprop --esp-page-sticky-header-top - Top inset for sticky headers.
@@ -230,6 +238,88 @@ type HeaderPosition = "normal" | "sticky" | "fixed";
230
238
  * @menuLabel Page
231
239
  * @menuIcon layout
232
240
  *
241
+ *
242
+ * @example Main, preview, and contextual help
243
+ * ```html
244
+ * <style>
245
+ * .page-workspace-demo {
246
+ * --esp-page-main-min-width: 26rem;
247
+ * --esp-page-main-max-width: 48rem;
248
+ * --esp-page-preview-min-width: 18rem;
249
+ * --esp-page-preview-max-width: 26rem;
250
+ * --esp-page-flyout-min-width: 18rem;
251
+ * --esp-page-flyout-max-width: 22rem;
252
+ * }
253
+ *
254
+ * .page-workspace-demo > main,
255
+ * .page-workspace-demo > [slot="preview"] {
256
+ * box-sizing: border-box;
257
+ * padding: var(--esp-size-padding-page);
258
+ * }
259
+ *
260
+ * .page-workspace-demo > main {
261
+ * min-block-size: 120vh;
262
+ * }
263
+ * </style>
264
+ *
265
+ * <esp-page
266
+ * id="page-workspace-demo"
267
+ * class="page-workspace-demo"
268
+ * preview-open
269
+ * preview-label="Rendered page preview"
270
+ * >
271
+ * <main>
272
+ * <h2>Page details</h2>
273
+ * <esp-form-item label="Site title">
274
+ * <esp-input id="page-workspace-title" name="title" value="Field notes"></esp-input>
275
+ * </esp-form-item>
276
+ * <p>
277
+ * Help is anchored to the title field in this main editor. Its caret and
278
+ * dotted connector cross the persistent preview without blocking it.
279
+ * </p>
280
+ * <esp-button id="page-workspace-preview-toggle" label="Toggle preview"></esp-button>
281
+ * <esp-button id="page-workspace-help-toggle" label="Toggle title help"></esp-button>
282
+ * </main>
283
+ *
284
+ * <article slot="preview">
285
+ * <h2>Field notes</h2>
286
+ * <p>This preview is a full-viewport secondary surface with its own scroller.</p>
287
+ * </article>
288
+ *
289
+ * <esp-flyout
290
+ * id="page-workspace-help"
291
+ * slot="flyout"
292
+ * heading="Site title help"
293
+ * standalone
294
+ * >
295
+ * <p>Use a short, recognizable title. It appears in navigation and browser tabs.</p>
296
+ * </esp-flyout>
297
+ * </esp-page>
298
+ *
299
+ * <script>
300
+ * const page = findById("page-workspace-demo");
301
+ * const title = findById("page-workspace-title");
302
+ * const help = findById("page-workspace-help");
303
+ * help.anchor = title;
304
+ *
305
+ * const syncMainWidth = () => {
306
+ * page.kind = page.previewOpen || help.open ? "wide" : "full";
307
+ * };
308
+ *
309
+ * findById("page-workspace-preview-toggle").addEventListener("clicked", () => {
310
+ * page.togglePreview();
311
+ * syncMainWidth();
312
+ * });
313
+ * findById("page-workspace-help-toggle").addEventListener("clicked", () => {
314
+ * help.anchor = title;
315
+ * help.toggle();
316
+ * syncMainWidth();
317
+ * });
318
+ * help.addEventListener("flyout-opened", syncMainWidth);
319
+ * help.addEventListener("flyout-closed", syncMainWidth);
320
+ * syncMainWidth();
321
+ * </script>
322
+ * ```
233
323
  */
234
324
  export declare class EspalierPage extends EspalierElementBase {
235
325
  constructor();
@@ -315,7 +405,7 @@ export declare class EspalierPage extends EspalierElementBase {
315
405
  previewVisible: boolean;
316
406
  /**
317
407
  * Whether preview is currently reserving width and allowing the main well
318
- * to shrink toward `--esp-page-preview-min-main-width`. Managed by the page.
408
+ * to shrink toward `--esp-page-main-min-width`. Managed by the page.
319
409
  */
320
410
  previewReclaiming: boolean;
321
411
  /** Request the persistent preview without moving focus. */