@taprootio/espalier 2.8.1 → 2.10.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.
@@ -1,14 +1,14 @@
1
- var i=function(h,e,a,s){var d=arguments.length,o=d<3?e:s===null?s=Object.getOwnPropertyDescriptor(e,a):s,t;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")o=Reflect.decorate(h,e,a,s);else for(var n=h.length-1;n>=0;n--)(t=h[n])&&(o=(d<3?t(o):d>3?t(e,a,o):t(e,a))||o);return d>3&&o&&Object.defineProperty(e,a,o),o};import{css as m,html as v,nothing as y}from"lit";import{customElement as w,property as l,state as b}from"lit/decorators.js";import{classMap as k}from"lit/directives/class-map.js";import{createRef as p,ref as c}from"lit/directives/ref.js";import{EspalierElementBase as g}from"../shared/esp-element-base.js";import{BiDirectionalStickyController as u}from"./bi-directional-sticky-controller.js";import{getEspBus as x}from"../shared/bus-events.js";import"../toaster/esp-toaster.js";const f='esp-footer, footer, [role~="contentinfo"]';let r=class extends g{constructor(){super(),this.dialogZone=p(),this.flyoutSlot=p(),this.footerSlot=p(),this.footerWrapperIsLandmark=!0,this.kind="wide",this.align="start",this.contained=!1,this.headerPosition="normal",this.fixedMenus=!1,new u(this,".esp-page-left > .sticky-wrapper"),new u(this,".esp-page-right > .sticky-wrapper"),new u(this,".esp-page-flyout > .sticky-wrapper"),this.addEventListener("flyout-state-changed",e=>this.syncFlyoutState(e))}connectedCallback(){const e=Array.from(this.children).filter(a=>a.getAttribute("slot")==="footer");this.footerWrapperIsLandmark=!e.some(a=>this.footerElementProvidesLandmark(a)),super.connectedCallback()}syncFlyoutState(e){if(e&&e.target?.closest?.("esp-page")!==this)return;const a=t=>{const n=t.mode;return(typeof n=="string"?n:t.getAttribute("mode"))==="overlay"},s=t=>{const n=t.open;return typeof n=="boolean"?n:t.hasAttribute("open")},d=t=>t.anchor!=null,o=(this.flyoutSlot.value?.assignedElements()??[]).filter(t=>t.tagName==="ESP-FLYOUT");this.toggleAttribute("flyout-open",o.some(t=>s(t)&&!a(t))),this.toggleAttribute("flyout-overlay-open",o.some(t=>s(t)&&a(t))),this.toggleAttribute("flyout-anchored",o.some(t=>s(t)&&d(t)))}footerElementProvidesLandmark(e){return e.matches(f)||e.querySelector(f)!==null}syncFooterLandmark(){const a=(this.footerSlot.value?.assignedElements({flatten:!0})??[]).some(s=>this.footerElementProvidesLandmark(s));this.footerWrapperIsLandmark=!a}AddDialog(e){this.dialogZone.value?.appendChild(e)}firstUpdated(e){super.firstUpdated(e),this.syncFlyoutState()}updated(e){super.updated(e),(e.has("fixedMenus")||e.has("headerPosition"))&&x().publish("fixed-menus-changed",{fixed:this.fixedMenus||this.headerPosition==="fixed"})}render(){const e=this.fixedMenus||this.headerPosition==="fixed",a=!e&&this.headerPosition==="sticky";return v`
2
- <div part="wrapper" class="esp-page ${k({"fixed-menus":e,"fixed-header":e,"sticky-header":a})}">
1
+ var p=function(u,t,e,a){var r=arguments.length,s=r<3?t:a===null?a=Object.getOwnPropertyDescriptor(t,e):a,i;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")s=Reflect.decorate(u,t,e,a);else for(var o=u.length-1;o>=0;o--)(i=u[o])&&(s=(r<3?i(s):r>3?i(t,e,s):i(t,e))||s);return r>3&&s&&Object.defineProperty(t,e,s),s};import{css as k,html as x,nothing as S}from"lit";import{customElement as P,property as l,state as C}from"lit/decorators.js";import{classMap as z}from"lit/directives/class-map.js";import{createRef as d,ref as h}from"lit/directives/ref.js";import{EspalierElementBase as f}from"../shared/esp-element-base.js";import{BiDirectionalStickyController as g}from"./bi-directional-sticky-controller.js";import{getEspBus as R}from"../shared/bus-events.js";import"../toaster/esp-toaster.js";const w='esp-footer, footer, [role~="contentinfo"]',_="(max-width: 50em)",c=1;let n=class extends f{constructor(){super(),this.dialogZone=d(),this.flyoutSlot=d(),this.footerSlot=d(),this.previewSlot=d(),this.previewSpace=d(),this.previewWidthProbe=d(),this.previewMainMinProbe=d(),this.previewObservedWidths=new WeakMap,this.previewSyncRaf=null,this.previewSyncGeneration=0,this.previewSyncTask=Promise.resolve(),this.previewHeader=null,this.previewMenu=null,this.previewCollapsedSidebarWidth=0,this.footerWrapperIsLandmark=!0,this.schedulePreviewLayout=()=>{if(!this.isConnected||!this.hasUpdated)return;this.previewSyncGeneration+=1;const t=this.previewSyncGeneration;this.previewSyncRaf!==null&&cancelAnimationFrame(this.previewSyncRaf),this.previewSyncRaf=requestAnimationFrame(()=>{this.previewSyncRaf=null,this.previewSyncTask=this.previewSyncTask.then(()=>this.syncPreviewLayout(t)).catch(()=>{})})},this.onPreviewMediaChange=()=>this.schedulePreviewLayout(),this.onPreviewResize=t=>{t.some(a=>{const r=a.contentRect.width,s=this.previewObservedWidths.get(a.target);return this.previewObservedWidths.set(a.target,r),s===void 0||Math.abs(s-r)>.25})&&this.schedulePreviewLayout()},this.kind="wide",this.align="start",this.contained=!1,this.headerPosition="normal",this.fixedMenus=!1,this.previewOpen=!1,this.previewLabel="Preview",this.previewCollapseSidebar=!1,this.previewVisible=!1,this.previewReclaiming=!1,new g(this,".esp-page-left > .sticky-wrapper"),new g(this,".esp-page-right > .sticky-wrapper"),new g(this,".esp-page-flyout > .sticky-wrapper"),new g(this,".esp-page-preview > .sticky-wrapper"),this.addEventListener("flyout-state-changed",t=>this.syncFlyoutState(t))}connectedCallback(){const t=Array.from(this.children).filter(e=>e.getAttribute("slot")==="footer");this.footerWrapperIsLandmark=!t.some(e=>this.footerElementProvidesLandmark(e)),super.connectedCallback(),this.hasUpdated&&queueMicrotask(()=>this.setupPreviewLayoutObserver())}disconnectedCallback(){this.previewResizeObserver?.disconnect(),this.previewResizeObserver=void 0,this.previewMediaQuery?.removeEventListener("change",this.onPreviewMediaChange),this.previewMediaQuery=void 0,this.previewSyncRaf!==null&&(cancelAnimationFrame(this.previewSyncRaf),this.previewSyncRaf=null),this.previewSyncGeneration+=1,this.toggleAttribute("data-preview-measuring",!1),this.setPreviewLayoutState(!1,!1),this.setPreviewNavigationCollapse(!1),super.disconnectedCallback()}syncFlyoutState(t){if(t&&t.target?.closest?.("esp-page")!==this)return;const e=i=>{const o=i.mode;return(typeof o=="string"?o:i.getAttribute("mode"))==="overlay"},a=i=>{const o=i.open;return typeof o=="boolean"?o:i.hasAttribute("open")},r=i=>i.anchor!=null,s=(this.flyoutSlot.value?.assignedElements()??[]).filter(i=>i.tagName==="ESP-FLYOUT");this.toggleAttribute("flyout-open",s.some(i=>a(i)&&!e(i))),this.toggleAttribute("flyout-overlay-open",s.some(i=>a(i)&&e(i))),this.toggleAttribute("flyout-anchored",s.some(i=>a(i)&&r(i))),this.schedulePreviewLayout()}previewHasContent(){const t=this.previewSlot.value?.assignedNodes({flatten:!0});return t?t.some(e=>e.nodeType===Node.ELEMENT_NODE||(e.textContent?.trim().length??0)>0):Array.from(this.childNodes).some(e=>e instanceof Element&&e.getAttribute("slot")==="preview"&&!e.hasAttribute("hidden"))}getPreviewNavigationPair(){if(!this.previewCollapseSidebar)return null;const t=Array.from(this.children),e=t.find(i=>i.slot==="header"&&i.tagName==="ESP-HEADER"),a=t.find(i=>i.slot==="sidebar"&&i.tagName==="ESP-MENU");if(!e||!a||!a.id)return null;const r=typeof e.drawerTarget=="string"?e.drawerTarget:e.getAttribute("drawer-target")??"",s=typeof a.mode=="string"?a.mode:a.getAttribute("mode");return r!==a.id||s!=="vertical"||!("previewCollapseRequested"in e)||!("previewCollapseRequested"in a)?null:{header:e,menu:a}}async setPreviewNavigationCollapse(t){const e=t?this.getPreviewNavigationPair():null,a=e!==null&&this.previewHeader===e.header&&this.previewMenu===e.menu,r=this.previewHeader&&this.previewHeader!==e?.header?this.previewHeader:null,s=this.previewMenu&&this.previewMenu!==e?.menu?this.previewMenu:null;if(r&&(r.previewCollapseRequested=!1),s&&(s.previewCollapseRequested=!1),this.previewHeader=e?.header??null,this.previewMenu=e?.menu??null,this.toggleAttribute("data-preview-navigation-collapsed",!1),await r?.updateComplete,await s?.updateComplete,!e)return this.previewCollapsedSidebarWidth=0,!1;const i=a?this.previewCollapsedSidebarWidth:this.shadowRoot?.querySelector(".esp-page-left")?.getBoundingClientRect().width??0;return e.header.previewCollapseRequested=!0,e.menu.previewCollapseRequested=!0,await e.header.updateComplete,await e.menu.updateComplete,e.menu.hasExternalDrawerControl===!0?(this.previewCollapsedSidebarWidth=i,this.toggleAttribute("data-preview-navigation-collapsed",!0),!0):(e.header.previewCollapseRequested=!1,e.menu.previewCollapseRequested=!1,await e.header.updateComplete,await e.menu.updateComplete,this.previewHeader=null,this.previewMenu=null,this.previewCollapsedSidebarWidth=0,!1)}previewTokenIsConfigured(){return getComputedStyle(this).getPropertyValue("--esp-page-preview-min-main-width").trim().length>0}setPreviewLayoutState(t,e){this.previewVisible=t,this.previewReclaiming=t&&e}previewFitsWithinPage(t,e){const a=this.shadowRoot?.querySelector(".esp-page-main"),r=this.shadowRoot?.querySelector(".esp-page-preview");if(!a||!r)return!1;const s=this.getBoundingClientRect(),i=a.getBoundingClientRect(),o=r.getBoundingClientRect();return i.width+c>=t&&o.width+c>=e&&o.left+c>=s.left&&o.right<=s.right+c}async syncPreviewLayout(t){if(t!==this.previewSyncGeneration||!this.isConnected)return;let e=!1;this.toggleAttribute("data-preview-measuring",!0);try{const a=this.previewOpen&&this.previewHasContent(),r=this.previewMediaQuery?.matches??!1,s=this.hasAttribute("flyout-overlay-open"),i=this.hasAttribute("data-preview-navigation-collapsed")?this.previewCollapsedSidebarWidth:0;if(this.setPreviewLayoutState(!1,!1),this.toggleAttribute("data-preview-navigation-collapsed",!1),await this.updateComplete,t!==this.previewSyncGeneration||!a||r||s)return;const o=this.previewWidthProbe.value?.getBoundingClientRect().width??0,m=this.previewSpace.value?.getBoundingClientRect().width??0,y=Math.max(0,m-i);if(o<=0)return;if(y+c>=o){this.setPreviewLayoutState(!0,!1);return}if(!this.previewTokenIsConfigured())return;const v=this.previewMainMinProbe.value?.getBoundingClientRect().width??0;if(v<=0||(e=await this.setPreviewNavigationCollapse(this.previewCollapseSidebar),t!==this.previewSyncGeneration)||(this.setPreviewLayoutState(!0,!0),await new Promise(b=>requestAnimationFrame(()=>b())),t!==this.previewSyncGeneration))return;this.previewFitsWithinPage(v,o)||(this.setPreviewLayoutState(!1,!1),e=!1)}finally{t===this.previewSyncGeneration&&!e&&await this.setPreviewNavigationCollapse(!1),t===this.previewSyncGeneration&&this.toggleAttribute("data-preview-measuring",!1)}}setupPreviewLayoutObserver(){this.isConnected&&(!this.previewMediaQuery&&typeof window.matchMedia=="function"&&(this.previewMediaQuery=window.matchMedia(_),this.previewMediaQuery.addEventListener("change",this.onPreviewMediaChange)),!this.previewResizeObserver&&typeof ResizeObserver<"u"&&(this.previewResizeObserver=new ResizeObserver(this.onPreviewResize),this.previewResizeObserver.observe(this),this.previewWidthProbe.value&&this.previewResizeObserver.observe(this.previewWidthProbe.value),this.previewMainMinProbe.value&&this.previewResizeObserver.observe(this.previewMainMinProbe.value)),this.schedulePreviewLayout())}footerElementProvidesLandmark(t){return t.matches(w)||t.querySelector(w)!==null}syncFooterLandmark(){const e=(this.footerSlot.value?.assignedElements({flatten:!0})??[]).some(a=>this.footerElementProvidesLandmark(a));this.footerWrapperIsLandmark=!e}showPreview(){this.previewOpen=!0}closePreview(){this.previewOpen=!1}togglePreview(){this.previewOpen=!this.previewOpen}AddDialog(t){this.dialogZone.value?.appendChild(t)}firstUpdated(t){super.firstUpdated(t),this.syncFlyoutState(),this.setupPreviewLayoutObserver()}updated(t){super.updated(t),(t.has("fixedMenus")||t.has("headerPosition"))&&R().publish("fixed-menus-changed",{fixed:this.fixedMenus||this.headerPosition==="fixed"}),(t.has("previewOpen")||t.has("previewCollapseSidebar"))&&this.schedulePreviewLayout()}render(){const t=this.fixedMenus||this.headerPosition==="fixed",e=!t&&this.headerPosition==="sticky";return x`
2
+ <div part="wrapper" class="esp-page ${z({"fixed-menus":t,"fixed-header":t,"sticky-header":e})}">
3
3
  <div part="canvas" class="esp-page-canvas esp-page-canvas--left" aria-hidden="true"></div>
4
4
  <div part="canvas" class="esp-page-canvas esp-page-canvas--right" aria-hidden="true"></div>
5
5
  <div part="surface" class="esp-page-surface" aria-hidden="true"></div>
6
6
  <div class="esp-page-top">
7
- <slot name="header"></slot>
7
+ <slot name="header" @slotchange=${this.schedulePreviewLayout}></slot>
8
8
  </div>
9
9
  <aside class="esp-page-left">
10
10
  <div class="sticky-wrapper">
11
- <slot name="sidebar"></slot>
11
+ <slot name="sidebar" @slotchange=${this.schedulePreviewLayout}></slot>
12
12
  </div>
13
13
  </aside>
14
14
  <div class="esp-page-main">
@@ -23,18 +23,39 @@ var i=function(h,e,a,s){var d=arguments.length,o=d<3?e:s===null?s=Object.getOwnP
23
23
  <div class="sticky-wrapper">
24
24
  <slot
25
25
  name="flyout"
26
- ${c(this.flyoutSlot)}
26
+ ${h(this.flyoutSlot)}
27
27
  @slotchange=${()=>this.syncFlyoutState()}
28
28
  ></slot>
29
29
  </div>
30
30
  </div>
31
- <div class="esp-page-footer" role=${this.footerWrapperIsLandmark?"contentinfo":y}>
32
- <slot name="footer" ${c(this.footerSlot)} @slotchange=${this.syncFooterLandmark}></slot>
31
+ <div
32
+ class="esp-page-preview-width-probe"
33
+ aria-hidden="true"
34
+ ${h(this.previewWidthProbe)}
35
+ ></div>
36
+ <div
37
+ class="esp-page-preview-main-min-probe"
38
+ aria-hidden="true"
39
+ ${h(this.previewMainMinProbe)}
40
+ ></div>
41
+ <div class="esp-page-preview-space" ${h(this.previewSpace)}>
42
+ <aside part="preview" class="esp-page-preview" aria-label=${this.previewLabel}>
43
+ <div part="preview-content" class="sticky-wrapper">
44
+ <slot
45
+ name="preview"
46
+ ${h(this.previewSlot)}
47
+ @slotchange=${this.schedulePreviewLayout}
48
+ ></slot>
49
+ </div>
50
+ </aside>
33
51
  </div>
34
- <div id="dialog-drop-zone" ${c(this.dialogZone)}></div>
52
+ <div class="esp-page-footer" role=${this.footerWrapperIsLandmark?"contentinfo":S}>
53
+ <slot name="footer" ${h(this.footerSlot)} @slotchange=${this.syncFooterLandmark}></slot>
54
+ </div>
55
+ <div id="dialog-drop-zone" ${h(this.dialogZone)}></div>
35
56
  <esp-toaster></esp-toaster>
36
57
  </div>
37
- `}};r.styles=[...g.styles,m`
58
+ `}};n.styles=[...f.styles,k`
38
59
  :host {
39
60
  --_esp-page-resolved-max-width: var(--esp-page-max-width, 1536px);
40
61
 
@@ -45,6 +66,8 @@ var i=function(h,e,a,s){var d=arguments.length,o=d<3?e:s===null?s=Object.getOwnP
45
66
 
46
67
  --_esp-page-flyout-min: 0px;
47
68
 
69
+ --_esp-page-preview-min: 0px;
70
+
48
71
  --_esp-page-surface-edge-shadow: 0.75rem 0 1.5rem -0.75rem var(--esp-color-shadow);
49
72
 
50
73
  --_esp-page-surface-shadow: var(
@@ -88,11 +111,31 @@ var i=function(h,e,a,s){var d=arguments.length,o=d<3?e:s===null?s=Object.getOwnP
88
111
  --_esp-page-flyout-min: var(--esp-page-flyout-width, 20rem);
89
112
  }
90
113
 
114
+ :host([preview-reclaiming]) {
115
+ --_esp-page-preview-min: var(--esp-page-preview-width, 20rem);
116
+ --_esp-page-main-track: minmax(
117
+ var(--esp-page-preview-min-main-width, 0px),
118
+ var(--_esp-page-resolved-max-width)
119
+ );
120
+ }
121
+
122
+ :host([kind="full"][preview-reclaiming]) {
123
+ --_esp-page-main-track: minmax(var(--esp-page-preview-min-main-width, 0px), 1fr);
124
+ }
125
+
91
126
 
92
127
  @media (max-width: 50em) {
93
128
  :host([flyout-open]) {
94
129
  --_esp-page-flyout-min: 0px;
95
130
  }
131
+
132
+ :host([preview-reclaiming]) {
133
+ --_esp-page-preview-min: 0px;
134
+ }
135
+
136
+ .esp-page > .esp-page-preview-space {
137
+ display: none;
138
+ }
96
139
  }
97
140
 
98
141
 
@@ -175,6 +218,20 @@ var i=function(h,e,a,s){var d=arguments.length,o=d<3?e:s===null?s=Object.getOwnP
175
218
  position: absolute;
176
219
  }
177
220
 
221
+ :host([preview-visible]) .esp-page > div.esp-page-preview-space > .esp-page-preview {
222
+ display: block;
223
+ }
224
+
225
+
226
+ :host([data-preview-navigation-collapsed]) .esp-page > aside.esp-page-left {
227
+ inline-size: 0;
228
+ min-inline-size: 0;
229
+ }
230
+
231
+ :host([data-preview-measuring]) .esp-page {
232
+ transition: none;
233
+ }
234
+
178
235
  .esp-page {
179
236
 
180
237
  min-height: 100vh;
@@ -186,7 +243,7 @@ var i=function(h,e,a,s){var d=arguments.length,o=d<3?e:s===null?s=Object.getOwnP
186
243
  [left-end main-start] var(--_esp-page-main-track)
187
244
  [main-end right-start] min-content
188
245
  [right-end surface-end canvas-right-start flyout-start] minmax(
189
- var(--_esp-page-flyout-min),
246
+ calc(var(--_esp-page-flyout-min) + var(--_esp-page-preview-min)),
190
247
  var(--_esp-page-gutter-right)
191
248
  )
192
249
  [flyout-end canvas-right-end full-end];
@@ -272,6 +329,41 @@ var i=function(h,e,a,s){var d=arguments.length,o=d<3?e:s===null?s=Object.getOwnP
272
329
  }
273
330
 
274
331
 
332
+ > div.esp-page-preview-space {
333
+ grid-column: flyout;
334
+ grid-row: content;
335
+ z-index: 4;
336
+ position: relative;
337
+ min-inline-size: 0;
338
+ margin-inline-start: var(--_esp-page-flyout-min);
339
+
340
+ > .esp-page-preview {
341
+ display: none;
342
+ inline-size: var(--esp-page-preview-width, 20rem);
343
+ max-inline-size: 100%;
344
+ position: relative;
345
+ }
346
+ }
347
+
348
+
349
+ > .esp-page-preview-width-probe,
350
+ > .esp-page-preview-main-min-probe {
351
+ position: absolute;
352
+ block-size: 0;
353
+ visibility: hidden;
354
+ pointer-events: none;
355
+ contain: strict;
356
+ }
357
+
358
+ > .esp-page-preview-width-probe {
359
+ inline-size: var(--esp-page-preview-width, 20rem);
360
+ }
361
+
362
+ > .esp-page-preview-main-min-probe {
363
+ inline-size: var(--esp-page-preview-min-main-width, 0px);
364
+ }
365
+
366
+
275
367
  > .esp-page-canvas {
276
368
 
277
369
  grid-row: top-start / footer-end;
@@ -359,6 +451,7 @@ var i=function(h,e,a,s){var d=arguments.length,o=d<3?e:s===null?s=Object.getOwnP
359
451
  > aside.esp-page-left,
360
452
  > aside.esp-page-right,
361
453
  > div.esp-page-flyout,
454
+ > div.esp-page-preview-space,
362
455
  > .esp-page-surface,
363
456
  > .esp-page-canvas {
364
457
  margin-top: var(--_esp-page-fixed-header-offset);
@@ -394,4 +487,4 @@ var i=function(h,e,a,s){var d=arguments.length,o=d<3?e:s===null?s=Object.getOwnP
394
487
  transition: none;
395
488
  }
396
489
  }
397
- `],i([b()],r.prototype,"footerWrapperIsLandmark",void 0),i([l({reflect:!0})],r.prototype,"kind",void 0),i([l({reflect:!0})],r.prototype,"align",void 0),i([l({type:Boolean,reflect:!0})],r.prototype,"contained",void 0),i([l({attribute:"header-position",reflect:!0})],r.prototype,"headerPosition",void 0),i([l({attribute:"fixed-menus",type:Boolean,reflect:!0})],r.prototype,"fixedMenus",void 0),r=i([w("esp-page")],r);export{r as EspalierPage};
490
+ `],p([C()],n.prototype,"footerWrapperIsLandmark",void 0),p([l({reflect:!0})],n.prototype,"kind",void 0),p([l({reflect:!0})],n.prototype,"align",void 0),p([l({type:Boolean,reflect:!0})],n.prototype,"contained",void 0),p([l({attribute:"header-position",reflect:!0})],n.prototype,"headerPosition",void 0),p([l({attribute:"fixed-menus",type:Boolean,reflect:!0})],n.prototype,"fixedMenus",void 0),p([l({attribute:"preview-open",type:Boolean,reflect:!0})],n.prototype,"previewOpen",void 0),p([l({attribute:"preview-label",type:String,reflect:!0})],n.prototype,"previewLabel",void 0),p([l({attribute:"preview-collapse-sidebar",type:Boolean,reflect:!0})],n.prototype,"previewCollapseSidebar",void 0),p([l({attribute:"preview-visible",type:Boolean,reflect:!0})],n.prototype,"previewVisible",void 0),p([l({attribute:"preview-reclaiming",type:Boolean,reflect:!0})],n.prototype,"previewReclaiming",void 0),n=p([P("esp-page")],n);export{n as EspalierPage};
@@ -29,6 +29,7 @@
29
29
  import { EspBus } from "./esp-bus.js";
30
30
  import type { ToastConfig } from "./toast-events.js";
31
31
  import type { FlyoutConfig } from "./flyout-events.js";
32
+ import type { HelpRequest, HelpState } from "./help-events.js";
32
33
  /**
33
34
  * Contract for elements that provide a seed color to their subtree.
34
35
  * Implemented by `EspalierRoot` (the global provider) and
@@ -115,6 +116,24 @@ export interface FlyoutEvents {
115
116
  "show-flyout": FlyoutConfig;
116
117
  "close-flyout": Record<string, never>;
117
118
  }
119
+ /**
120
+ * URL help-system coordination events.
121
+ *
122
+ * `request-help` is published by {@link requestHelp} and serviced by a
123
+ * connected `<esp-help-provider>`. `close-help` asks that provider to close
124
+ * only the flyout presenting the matching topic. The provider publishes
125
+ * `help-state-changed` whenever a topic opens, swaps, or closes so all
126
+ * matching trigger affordances can synchronize `aria-pressed`.
127
+ *
128
+ * @docUrl /api/help-events
129
+ * @menuGroup Bus Events
130
+ * @menuLabel HelpEvents
131
+ */
132
+ export interface HelpEvents {
133
+ "request-help": HelpRequest;
134
+ "close-help": Pick<HelpRequest, "src" | "anchor">;
135
+ "help-state-changed": HelpState;
136
+ }
118
137
  /**
119
138
  * Cross-popover coordination events.
120
139
  *
@@ -181,7 +200,7 @@ export interface PageEventMap {
181
200
  * @menuGroup Bus Events
182
201
  * @menuLabel EspBusEventMap
183
202
  */
184
- export type EspBusEventMap = SchemeEvents & ToastEvents & FlyoutEvents & PopoverEvents & SizeEvents & PageEventMap;
203
+ export type EspBusEventMap = SchemeEvents & ToastEvents & FlyoutEvents & HelpEvents & PopoverEvents & SizeEvents & PageEventMap;
185
204
  /**
186
205
  * Typed accessor for the singleton bus.
187
206
  *
@@ -2,5 +2,5 @@ export declare class EspBus<TEventMap extends Record<string, any>> {
2
2
  static getInstance<TBus extends Record<string, any>>(): EspBus<TBus>;
3
3
  subscribe<K extends keyof TEventMap>(event: K, callback: (payload: TEventMap[K]) => void): void;
4
4
  unsubscribe<K extends keyof TEventMap>(event: K, callback: (payload: TEventMap[K]) => void): void;
5
- publish<K extends keyof TEventMap>(event: K, payload: TEventMap[K]): void;
5
+ publish<K extends keyof TEventMap>(event: K, payload: TEventMap[K]): number;
6
6
  }
@@ -1 +1 @@
1
- class i{constructor(){this.subscribers={}}static getInstance(){return i.instanceBacker||(i.instanceBacker=new i),i.instanceBacker}subscribe(s,r){this.subscribers[s]||(this.subscribers[s]=[]),this.subscribers[s].push(r)}unsubscribe(s,r){this.subscribers[s]&&(this.subscribers[s]=this.subscribers[s].filter(b=>b!==r))}publish(s,r){this.subscribers[s]&&this.subscribers[s].forEach(b=>b(r))}}export{i as EspBus};
1
+ class i{constructor(){this.subscribers={}}static getInstance(){return i.instanceBacker||(i.instanceBacker=new i),i.instanceBacker}subscribe(s,r){this.subscribers[s]||(this.subscribers[s]=[]),this.subscribers[s].push(r)}unsubscribe(s,r){this.subscribers[s]&&(this.subscribers[s]=this.subscribers[s].filter(c=>c!==r))}publish(s,r){const c=this.subscribers[s]??[],b=c.length;return c.forEach(t=>t(r)),b}}export{i as EspBus};
@@ -27,6 +27,7 @@ export { type DestroyEspalierInfo } from "../info/esp-info.js";
27
27
  export { type ValidityChangedDetail, type ValidationError, VALIDITY_CHANGED_EVENT, } from "./validation.js";
28
28
  export { type ToastConfig } from "./toast-events.js";
29
29
  export { type FlyoutConfig, type FlyoutCloseReason } from "./flyout-events.js";
30
+ export { type HelpRequest, type HelpState } from "./help-events.js";
30
31
  export { type PickerItem } from "../pickers/esp-picker-item.js";
31
32
  export { type EspalierUploadImage } from "../image-upload/image-helpers.js";
32
33
  export { type FontPickerValueChangedDetail, type GoogleFont, type WebSafeFont, } from "../font-picker/esp-font-picker.js";
@@ -37,14 +37,21 @@ export type FlyoutConfig = {
37
37
  * has (e.g. declaratively slotted markup).
38
38
  */
39
39
  content?: string | Node | undefined;
40
+ /**
41
+ * Fill the visible block-size in gutter and docked modes instead of
42
+ * sizing the panel to its content. The content region scrolls
43
+ * independently. Overlay drawers already fill the viewport.
44
+ */
45
+ fullHeight?: boolean | undefined;
40
46
  /**
41
47
  * Triggering element whose block-start edge the in-grid flyout
42
48
  * should align with. The flyout stays in the page's normal scroll
43
49
  * flow, shifting upward only while needed to fit the visible
44
50
  * scrollport. Content taller than that scrollport scrolls inside the
45
51
  * panel. Overlay drawers ignore this geometry and remain
46
- * viewport-fixed. When omitted, the flyout starts at the top of the
47
- * page's content row.
52
+ * viewport-fixed. Its outermost containing `esp-page` also supplies
53
+ * the default flyout scope. When omitted, the flyout starts at the
54
+ * top of the page's content row.
48
55
  */
49
56
  anchor?: HTMLElement | undefined;
50
57
  /**
@@ -66,9 +73,13 @@ export type FlyoutConfig = {
66
73
  * showFlyout({ heading: "Details", content: detailsFragment });
67
74
  * ```
68
75
  *
69
- * If no `<esp-flyout>` is connected, the event goes unserviced
70
- * callers that need a fallback (e.g. opening a URL in a new tab)
71
- * should check for a connected flyout themselves.
76
+ * Anchored requests target the outermost containing `esp-page` by
77
+ * default; a nested flyout with `scope="nearest"` explicitly overrides
78
+ * that routing. The nearest-scope flyout must be a light-DOM descendant
79
+ * of the page whose requests it owns. If no eligible `<esp-flyout>` is
80
+ * connected, the event goes unserviced — callers that need a fallback
81
+ * (e.g. opening a URL in a new tab) should check for a connected flyout
82
+ * themselves.
72
83
  *
73
84
  * @param config - Flyout configuration.
74
85
  * @docUrl /api/show-flyout
@@ -1 +1 @@
1
- import{getEspBus as o}from"./bus-events.js";function l(t={}){o().publish("show-flyout",t)}function s(){o().publish("close-flyout",{})}export{s as closeFlyout,l as showFlyout};
1
+ import{getEspBus as o}from"./bus-events.js";const u=new WeakSet,r="flyout-full-height-requested";function n(t={}){s(t)}function s(t={}){const e={...t};return o().publish("show-flyout",e),u.has(e)}function c(t){u.add(t)}function i(){o().publish("close-flyout",{})}export{r as FLYOUT_FULL_HEIGHT_REQUEST_EVENT,i as closeFlyout,c as markFlyoutRequestServiced,s as publishFlyoutRequest,n as showFlyout};
@@ -0,0 +1,55 @@
1
+ /**
2
+ * A request for one topic in a URL-addressable help document.
3
+ *
4
+ * @docUrl /api/help-request
5
+ * @menuGroup Configuration
6
+ * @menuLabel HelpRequest
7
+ */
8
+ export type HelpRequest = {
9
+ /** Absolute document URL without a fragment. */
10
+ src: string;
11
+ /** Stable heading id to show first. */
12
+ anchor?: string | undefined;
13
+ /** Explicit flyout title. */
14
+ title?: string | undefined;
15
+ /** Element whose bounds the contextual flyout should align with. */
16
+ placementTarget?: HTMLElement | undefined;
17
+ /** Originating control used for focus restoration. */
18
+ trigger?: HTMLElement | undefined;
19
+ };
20
+ /**
21
+ * Current help-topic state published by `esp-help-provider`.
22
+ *
23
+ * @docUrl /api/help-state
24
+ * @menuGroup Event Details
25
+ * @menuLabel HelpState
26
+ */
27
+ export type HelpState = {
28
+ src: string;
29
+ anchor?: string | undefined;
30
+ open: boolean;
31
+ };
32
+ /**
33
+ * Request help through the shared provider. Relative `src` values are
34
+ * resolved against the current document; a fragment becomes `anchor`
35
+ * unless the explicit property wins. When no provider is connected,
36
+ * the resolved topic opens in a new tab.
37
+ *
38
+ * ```ts
39
+ * import { requestHelp } from "@taprootio/espalier";
40
+ *
41
+ * requestHelp({
42
+ * src: "/help/account.html",
43
+ * anchor: "display-name",
44
+ * title: "Account help",
45
+ * placementTarget: document.querySelector<HTMLElement>("#display-name") ?? undefined,
46
+ * trigger: document.querySelector("#account-help") ?? undefined,
47
+ * });
48
+ * ```
49
+ *
50
+ * @param config - Help document, topic, title, and optional placement/focus targets.
51
+ * @docUrl /api/request-help
52
+ * @menuGroup Functions
53
+ * @menuLabel requestHelp
54
+ */
55
+ export declare function requestHelp(config: HelpRequest): void;
@@ -0,0 +1 @@
1
+ import{getEspBus as c}from"./bus-events.js";function i(e){const r=u(e);c().publish("request-help",r)===0&&s(r)}function s(e){const r=a(e);window.open(r.href,"_blank","noopener,noreferrer")}function u(e){const r=new URL(e.src,document.baseURI),t=l(r.hash);r.hash="";const n=e.anchor?.trim()||t,o=e.title?.trim()||void 0;return{src:r.href,...n?{anchor:n}:{},...o?{title:o}:{},...e.placementTarget?{placementTarget:e.placementTarget}:{},...e.trigger?{trigger:e.trigger}:{}}}function a(e){const r=new URL(e.src,document.baseURI);return e.anchor&&(r.hash=e.anchor),r}function l(e){const r=e.replace(/^#/,"").trim();if(r)try{return decodeURIComponent(r)}catch{return r}}export{l as decodeFragment,s as openHelpInNewTab,i as requestHelp};
@@ -1639,6 +1639,18 @@
1639
1639
  "component": "esp-page",
1640
1640
  "description": "The maximum width of the main content well. Set automatically by the `kind` attribute: `1536px` (wide), `768px` (narrow), or `none` (full). This cap now sizes the main grid track; surplus width beyond it becomes the canvas gutters."
1641
1641
  },
1642
+ {
1643
+ "name": "--esp-page-preview-min-main-width",
1644
+ "category": "component",
1645
+ "component": "esp-page",
1646
+ "description": "Optional minimum width of the main content well while preview is reclaiming canvas/surface space. Unset by default, so preview uses spare canvas only. Set a length such as `900px` to opt into reclaim behavior."
1647
+ },
1648
+ {
1649
+ "name": "--esp-page-preview-width",
1650
+ "category": "component",
1651
+ "component": "esp-page",
1652
+ "description": "Width of the persistent preview panel. Defaults to `20rem`."
1653
+ },
1642
1654
  {
1643
1655
  "name": "--esp-page-sticky-header-top",
1644
1656
  "category": "component",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taprootio/espalier",
3
- "version": "2.8.1",
3
+ "version": "2.10.0",
4
4
  "packageManager": "bun@1.3.12",
5
5
  "description": "Espalier — a themeable, accessible, framework-agnostic, enterprise-grade design system built on web standards and love.",
6
6
  "customElements": "custom-elements.json",
@@ -130,6 +130,14 @@
130
130
  "types": "./dist/header/index.d.ts",
131
131
  "import": "./dist/header/index.js"
132
132
  },
133
+ "./help/button": {
134
+ "types": "./dist/help/esp-help-button.d.ts",
135
+ "import": "./dist/help/esp-help-button.js"
136
+ },
137
+ "./help/provider": {
138
+ "types": "./dist/help/esp-help-provider.d.ts",
139
+ "import": "./dist/help/esp-help-provider.js"
140
+ },
133
141
  "./image": {
134
142
  "types": "./dist/image/esp-image.d.ts",
135
143
  "import": "./dist/image/esp-image.js"