@taprootio/espalier 2.8.0 → 2.9.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.
@@ -0,0 +1,49 @@
1
+ var i=function(s,e,t,l){var a=arguments.length,o=a<3?e:l===null?l=Object.getOwnPropertyDescriptor(e,t):l,c;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")o=Reflect.decorate(s,e,t,l);else for(var p=s.length-1;p>=0;p--)(c=s[p])&&(o=(a<3?c(o):a>3?c(e,t,o):c(e,t))||o);return a>3&&o&&Object.defineProperty(e,t,o),o};import{css as d,html as b,nothing as f}from"lit";import{customElement as g,property as n}from"lit/decorators.js";import{EspalierElementBase as u}from"../shared/esp-element-base.js";import{getEspBus as h}from"../shared/bus-events.js";import{requestHelp as m}from"../shared/help-events.js";import{getIconHrefForHost as v}from"../shared/intent-values.js";import{renderSpriteIcon as y}from"../shared/svgs/render-sprite-icon.js";import{resolveHelpTarget as T}from"./help-document.js";let r=class extends u{constructor(){super(...arguments),this.helpUrl="",this.anchor="",this.label="Help",this.helpTitle="",this.active=!1,this.boundHelpStateChanged=e=>{const t=this.resolvedTarget;this.active=!!(e.open&&t&&t.src===e.src&&(t.anchor??"")===(e.anchor??""))},this.handleActivation=()=>{if(this.active){const t=this.resolvedTarget;if(!t)return;h().publish("close-help",{src:t.src,...t.anchor?{anchor:t.anchor}:{}});return}const e=this.resolvedTarget;e&&m({src:e.src,...e.anchor?{anchor:e.anchor}:{},...this.helpTitle.trim()?{title:this.helpTitle.trim()}:{},...this.placementTarget?{placementTarget:this.placementTarget}:{},trigger:this})}}get resolvedTarget(){return T(this,{helpUrl:this.helpUrl,anchor:this.anchor})}connectedCallback(){super.connectedCallback(),h().subscribe("help-state-changed",this.boundHelpStateChanged)}disconnectedCallback(){h().unsubscribe("help-state-changed",this.boundHelpStateChanged),super.disconnectedCallback()}focus(e){this.shadowRoot?.querySelector("button")?.focus(e)}updated(e){super.updated(e),(e.has("helpUrl")||e.has("anchor"))&&(this.active=!1)}render(){if(!this.resolvedTarget)return f;const t=this.getAttribute("aria-label")?.trim()||this.label||"Help",l=v("info",this);return b`
2
+ <button
3
+ type="button"
4
+ aria-label=${t}
5
+ aria-pressed=${this.active?"true":"false"}
6
+ @click=${this.handleActivation}
7
+ >
8
+ ${y(l)}
9
+ </button>
10
+ `}};r.styles=[...u.styles,d`
11
+ :host {
12
+ display: inline-flex;
13
+ vertical-align: middle;
14
+ }
15
+
16
+ button {
17
+ appearance: none;
18
+ background: transparent;
19
+ border: 0;
20
+ border-radius: 0;
21
+ color: var(--esp-color-link);
22
+ cursor: pointer;
23
+ display: inline-grid;
24
+ font: inherit;
25
+ line-height: 1;
26
+ margin: 0;
27
+ padding: 0;
28
+ place-items: center;
29
+ transition:
30
+ background-color 0.15s ease,
31
+ color 0.15s ease;
32
+
33
+ .generated-icon {
34
+ block-size: 1em;
35
+ inline-size: 1em;
36
+ }
37
+
38
+ &:hover,
39
+ &[aria-pressed="true"] {
40
+ background: var(--esp-color-link-hover-bg);
41
+ color: var(--esp-color-link-hover);
42
+ }
43
+
44
+ &:focus-visible {
45
+ outline: 2px solid var(--esp-color-link);
46
+ outline-offset: 2px;
47
+ }
48
+ }
49
+ `],i([n({attribute:"help-url",type:String})],r.prototype,"helpUrl",void 0),i([n({type:String})],r.prototype,"anchor",void 0),i([n({type:String})],r.prototype,"label",void 0),i([n({attribute:"title",type:String})],r.prototype,"helpTitle",void 0),i([n({attribute:!1})],r.prototype,"placementTarget",void 0),i([n({type:Boolean,reflect:!0})],r.prototype,"active",void 0),r=i([g("esp-help-button")],r);export{r as EspalierHelpButton};
@@ -0,0 +1,40 @@
1
+ import { EspalierElementBase } from "../shared/esp-element-base.js";
2
+ /**
3
+ * Headless provider for URL-addressable help documents. Place one provider
4
+ * inside the same `<esp-root>` as the page's shared `<esp-flyout>`. It
5
+ * services `requestHelp()` calls, fetches and normalizes the document, and
6
+ * swaps an internal help view into the generic flyout surface.
7
+ *
8
+ * Successful documents are cached by absolute source URL. A newer request
9
+ * aborts an older in-flight fetch, so help always reflects the most recent
10
+ * trigger. If no flyout services the provider's presentation request, the
11
+ * original URL opens in a new tab. Fetch failures stay in the flyout and
12
+ * offer an explicit "Open help document" action.
13
+ *
14
+ * ```html
15
+ * <esp-root>
16
+ * <esp-help-provider></esp-help-provider>
17
+ * <esp-page help-src="/help/account.html">
18
+ * <esp-flyout slot="flyout"></esp-flyout>
19
+ * <!-- page content -->
20
+ * </esp-page>
21
+ * </esp-root>
22
+ * ```
23
+ *
24
+ * @docPageTitle Help Provider
25
+ * @docUrl /components/help/provider
26
+ * @menuGroup Feedback
27
+ * @menuLabel Help Provider
28
+ * @menuIcon info
29
+ */
30
+ export declare class EspalierHelpProvider extends EspalierElementBase {
31
+ connectedCallback(): void;
32
+ disconnectedCallback(): void;
33
+ protected render(): symbol;
34
+ static styles: import("lit").CSSResult[];
35
+ }
36
+ declare global {
37
+ interface HTMLElementTagNameMap {
38
+ "esp-help-provider": EspalierHelpProvider;
39
+ }
40
+ }
@@ -0,0 +1,5 @@
1
+ var d=function(r,e,t,n){var o=arguments.length,s=o<3?e:n===null?n=Object.getOwnPropertyDescriptor(e,t):n,i;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")s=Reflect.decorate(r,e,t,n);else for(var a=r.length-1;a>=0;a--)(i=r[a])&&(s=(o<3?i(s):o>3?i(e,t,s):i(e,t))||s);return o>3&&s&&Object.defineProperty(e,t,s),s};import{css as p,nothing as b}from"lit";import{customElement as f}from"lit/decorators.js";import{EspalierElementBase as v}from"../shared/esp-element-base.js";import{getEspBus as c}from"../shared/bus-events.js";import{publishFlyoutRequest as m}from"../shared/flyout-events.js";import{openHelpInNewTab as h}from"../shared/help-events.js";import{createHelpDocumentView as C,createHelpStatusView as u,normalizeHelpDocument as R}from"./help-document.js";let l=class extends v{constructor(){super(...arguments),this.cache=new Map,this.warnedMissingAnchors=new Set,this.activeRequest=null,this.activeController=null,this.activeContent=null,this.contentObserver=new MutationObserver(()=>{this.activeContent&&!this.activeContent.isConnected&&this.endActiveRequest()}),this.requestSequence=0,this.subscribed=!1,this.boundRequest=e=>{this.handleRequest(e)},this.boundCloseRequest=e=>{let t;try{t=new URL(e.src,document.baseURI).href}catch{return}if(!this.activeRequest||t!==this.activeRequest.src||(e.anchor??"")!==(this.activeRequest.anchor??""))return;const n=this.activeContent?.parentElement;n?.localName==="esp-flyout"&&n.close("programmatic")},this.boundFlyoutClosed=e=>{const t=e.target;!(t instanceof Element)||!this.activeContent||t.contains(this.activeContent)&&this.endActiveRequest()}}connectedCallback(){super.connectedCallback(),this.subscribed||(c().subscribe("request-help",this.boundRequest),c().subscribe("close-help",this.boundCloseRequest),this.subscribed=!0),document.addEventListener("flyout-closed",this.boundFlyoutClosed)}disconnectedCallback(){this.subscribed&&(c().unsubscribe("request-help",this.boundRequest),c().unsubscribe("close-help",this.boundCloseRequest),this.subscribed=!1),document.removeEventListener("flyout-closed",this.boundFlyoutClosed),this.endActiveRequest(),super.disconnectedCallback()}render(){return b}async handleRequest(e){const t=++this.requestSequence;this.activeController?.abort();const n=new AbortController;if(this.activeController=n,this.activeRequest=e,this.publishState(e,!0),this.showInFlyout(e,{heading:e.title??"Help",content:u({message:"Loading help\u2026"})})===0){this.endActiveRequest(),h(e);return}try{const s=this.cache.get(e.src)??await this.fetchDocument(e,n);if(t!==this.requestSequence||n.signal.aborted)return;if(!this.activeContent?.isConnected){this.endActiveRequest();return}this.cache.set(e.src,s);const i=C(s,e.anchor);this.warnForMissingAnchor(e,i.anchorFound),this.showInFlyout(e,{heading:e.title??i.topicTitle??s.title??"Help",content:i.element,fullHeight:i.fullDocument})}catch(s){if(t!==this.requestSequence||n.signal.aborted||w(s))return;if(!this.activeContent?.isConnected){this.endActiveRequest();return}this.showInFlyout(e,{heading:e.title??"Help unavailable",content:u({message:"Help could not be loaded.",actionLabel:"Open help document",onAction:()=>h(e)})})}finally{t===this.requestSequence&&(this.activeController=null)}}async fetchDocument(e,t){const n=await fetch(e.src,{signal:t.signal});if(!n.ok)throw new Error(`Help request failed with HTTP ${n.status}.`);return R(await n.text(),n.url||e.src)}showInFlyout(e,t){this.contentObserver.disconnect(),this.activeContent=t.content instanceof HTMLElement?t.content:null;const n=m({...t,anchor:e.placementTarget??e.trigger,returnFocusTo:e.trigger}),o=this.activeContent?.parentElement;return n&&o&&this.contentObserver.observe(o,{childList:!0}),n?1:0}publishState(e,t){c().publish("help-state-changed",{src:e.src,...e.anchor?{anchor:e.anchor}:{},open:t})}publishClosedState(){this.activeRequest&&(this.publishState(this.activeRequest,!1),this.activeRequest=null)}endActiveRequest(){(this.activeRequest||this.activeController)&&++this.requestSequence,this.activeController?.abort(),this.activeController=null,this.contentObserver.disconnect(),this.activeContent=null,this.publishClosedState()}warnForMissingAnchor(e,t){if(!e.anchor||t)return;const n=`${e.src}#${e.anchor}`;this.warnedMissingAnchors.has(n)||(this.warnedMissingAnchors.add(n),console.warn(`Help anchor "${e.anchor}" was not found in ${e.src}; showing the full document.`))}};l.styles=[p`
2
+ :host {
3
+ display: none;
4
+ }
5
+ `],l=d([f("esp-help-provider")],l);function w(r){return r instanceof DOMException&&r.name==="AbortError"}export{l as EspalierHelpProvider};
@@ -0,0 +1,52 @@
1
+ import"../button/esp-button.js";import{FLYOUT_FULL_HEIGHT_REQUEST_EVENT as w}from"../shared/flyout-events.js";import{decodeFragment as E}from"../shared/help-events.js";const v=new Set(["A","ABBR","B","BLOCKQUOTE","BR","CODE","DD","DEL","DETAILS","DL","DT","EM","FIGCAPTION","FIGURE","H1","H2","H3","H4","H5","H6","HR","I","IMG","KBD","LI","MARK","OL","P","PRE","Q","S","SAMP","SMALL","STRONG","SUB","SUMMARY","SUP","TABLE","TBODY","TD","TFOOT","TH","THEAD","TR","U","UL","VAR"]),N=new Set(["SCRIPT","STYLE","TEMPLATE","NOSCRIPT"]),y={A:["href"],DETAILS:["open"],IMG:["src","alt","width","height"],LI:["value"],OL:["start","reversed"],TD:["colspan","rowspan"],TH:["colspan","rowspan","scope"]},g=`
2
+ :host {
3
+ color: var(--esp-color-text);
4
+ display: block;
5
+ font-family: var(--esp-font-body);
6
+ font-size: var(--esp-size-font);
7
+ line-height: 1.5;
8
+ }
9
+
10
+ [part="content"] {
11
+ overflow-wrap: anywhere;
12
+ }
13
+
14
+ [part="content"] > :first-child {
15
+ margin-block-start: 0;
16
+ }
17
+
18
+ [part="content"] > :last-child {
19
+ margin-block-end: 0;
20
+ }
21
+
22
+ h1, h2, h3, h4, h5, h6 {
23
+ color: var(--esp-color-headings, var(--esp-color-text));
24
+ font-family: var(--esp-font-headings, var(--esp-font-body));
25
+ line-height: 1.2;
26
+ }
27
+
28
+ img {
29
+ block-size: auto;
30
+ max-inline-size: 100%;
31
+ }
32
+
33
+ pre {
34
+ max-inline-size: 100%;
35
+ overflow: auto;
36
+ }
37
+
38
+ table {
39
+ border-collapse: collapse;
40
+ inline-size: 100%;
41
+ }
42
+
43
+ th, td {
44
+ border: 1px solid var(--esp-color-border);
45
+ padding: var(--esp-size-tiny-to-small);
46
+ text-align: start;
47
+ }
48
+
49
+ [part="actions"] {
50
+ margin-block-start: var(--esp-size-padding);
51
+ }
52
+ `;function P(e,t={}){const n=t.helpUrl?.trim()??"",o=n?"":H(e),r=n||o;if(!r)return null;let c;try{c=new URL(r,e.ownerDocument.baseURI)}catch{return null}const i=E(c.hash);c.hash="";const a=m(t.anchor,t.fallbackAnchor),s=n?m(i,a):m(a,i),u=new URL(c.href);return s&&(u.hash=s),{src:c.href,...s?{anchor:s}:{},href:u.href}}function k(e,t){const n=new URL(t,document.baseURI);n.hash="";const o=new DOMParser().parseFromString(e,"text/html"),r=o.querySelector("title")?.textContent?.trim()||void 0,c=document.createDocumentFragment();for(const i of[...o.body.childNodes])b(i,c,n.href);return{src:n.href,...r?{title:r}:{},body:c}}function D(e,t){const n=t?.trim()??"";if(!n)return{content:e.body.cloneNode(!0),anchorFound:!0,sectioned:!1};const o=T(e.body,n);if(!o)return{content:e.body.cloneNode(!0),anchorFound:!1,sectioned:!1};const r=A(o);if(!r||!o.parentNode)return{content:e.body.cloneNode(!0),anchorFound:!0,sectioned:!1};const c=document.createDocumentFragment();let i=o.nextSibling;for(;i;){if(i instanceof Element){const a=A(i);if(a&&a<=r)break}c.append(i.cloneNode(!0)),i=i.nextSibling}return{content:c,anchorFound:!0,sectioned:!0,heading:o.textContent?.trim()||void 0}}function B(e,t){const n=t?.trim()||void 0,o=D(e,n),r=document.createElement("section");r.dataset.espHelpDocument="";const c=r.attachShadow({mode:"open"}),i=document.createElement("style");i.textContent=g;const a=document.createElement("div");a.setAttribute("part","content");const s=document.createElement("div");s.setAttribute("part","actions");const u=d=>{a.replaceChildren(e.body.cloneNode(!0)),l?.setAttribute("disabled",""),l?.setAttribute("label","Full document"),r.dispatchEvent(new CustomEvent(w,{bubbles:!0,composed:!0})),d&&queueMicrotask(()=>S(a,d))};let l=null;return o.sectioned?(a.append(o.content),l=document.createElement("esp-button"),l.setAttribute("label","View full document"),l.setAttribute("collapsed",""),l.setAttribute("incognito",""),l.addEventListener("clicked",()=>u(n)),s.append(l)):a.append(o.content),a.addEventListener("click",d=>{const f=d.composedPath().find(L=>L instanceof HTMLAnchorElement)?.getAttribute("href")??"";if(!f)return;if(!f.startsWith("#")){d.preventDefault(),window.open(f,"_blank","noopener,noreferrer");return}d.preventDefault();const p=E(f);p&&(o.sectioned?u(p):S(a,p))}),c.append(i,a,s),{element:r,anchorFound:o.anchorFound,fullDocument:!o.sectioned,...o.heading?{topicTitle:o.heading}:{}}}function M(e){const t=document.createElement("section");t.dataset.espHelpStatus="";const n=t.attachShadow({mode:"open"}),o=document.createElement("style");o.textContent=g;const r=document.createElement("p");if(r.setAttribute("part","message"),r.textContent=e.message,n.append(o,r),e.actionLabel&&e.onAction){const c=document.createElement("div");c.setAttribute("part","actions");const i=document.createElement("esp-button");i.setAttribute("label",e.actionLabel),i.setAttribute("collapsed",""),i.addEventListener("clicked",e.onAction),c.append(i),n.append(c)}return t}function H(e){let t=e;const n=new Set;for(;t&&!n.has(t);){n.add(t);const o=t.getAttribute("help-src")?.trim()??"";if(o)return o;t=h(t)}return""}function h(e){if(e.assignedSlot)return e.assignedSlot;if(e.parentElement)return e.parentElement;const t=e.getRootNode();return t instanceof ShadowRoot?t.host:null}function m(...e){for(const t of e){const n=t?.trim()??"";if(n)return n}}function b(e,t,n){if(e.nodeType===Node.TEXT_NODE){t.appendChild(document.createTextNode(e.textContent??""));return}if(!(e instanceof Element)||N.has(e.tagName))return;if(!v.has(e.tagName)){for(const r of[...e.childNodes])b(r,t,n);return}const o=document.createElement(e.tagName.toLowerCase());R(e,o,n);for(const r of[...e.childNodes])b(r,o,n);t.appendChild(o)}function R(e,t,n){const o=e.getAttribute("id")?.trim();o&&t.setAttribute("id",o);for(const r of y[e.tagName]??[]){if(!e.hasAttribute(r))continue;const c=e.getAttribute(r)??"",i=r==="href"||r==="src"?C(c,n):c;i!==null&&t.setAttribute(r,i)}}function C(e,t){if(!e||e.startsWith("#"))return e;try{const n=new URL(e,t);return["http:","https:","mailto:","tel:"].includes(n.protocol)?n.href:null}catch{return null}}function T(e,t){return[...e.querySelectorAll("[id]")].find(n=>n.id===t)??null}function A(e){const t=/^H([1-6])$/u.exec(e.tagName);return t?Number(t[1]):null}function S(e,t){const n=T(e,t);if(!n)return;const o=x(n);if(!o)return;const r=n.getBoundingClientRect().top,c=o.getBoundingClientRect().top;o.scrollTop+=r-c}function x(e){for(let t=h(e);t;t=h(t)){if(!(t instanceof HTMLElement))continue;const n=getComputedStyle(t).overflowY;if(n==="auto"||n==="scroll")return t}return null}export{B as createHelpDocumentView,M as createHelpStatusView,k as normalizeHelpDocument,P as resolveHelpTarget,D as selectHelpContent};
package/dist/index.d.ts CHANGED
@@ -17,6 +17,8 @@ export * from "./color-picker/esp-color-picker.js";
17
17
  export * from "./flyout/esp-flyout.js";
18
18
  export * from "./form/esp-form.js";
19
19
  export * from "./form-item/esp-form-item.js";
20
+ export * from "./help/esp-help-button.js";
21
+ export * from "./help/esp-help-provider.js";
20
22
  export * from "./header/index.js";
21
23
  export * from "./page/esp-page.js";
22
24
  export * from "./popover/esp-popover.js";
@@ -62,7 +64,8 @@ export { FormFieldController, type FormFieldControllerOptions, } from "./shared/
62
64
  export { traverseToClosest } from "./shared/utilities.js";
63
65
  export { showToast, type ToastConfig } from "./shared/toast-events.js";
64
66
  export { showFlyout, closeFlyout, type FlyoutConfig, type FlyoutCloseReason, } from "./shared/flyout-events.js";
65
- export { getEspBus, type EspBusEventMap, type SchemeEvents, type ToastEvents, type FlyoutEvents, type PopoverEvents, type SizeEvents, type PageEventMap, type SeedColorRoot, } from "./shared/bus-events.js";
67
+ export { requestHelp, type HelpRequest, type HelpState } from "./shared/help-events.js";
68
+ export { getEspBus, type EspBusEventMap, type SchemeEvents, type ToastEvents, type FlyoutEvents, type HelpEvents, type PopoverEvents, type SizeEvents, type PageEventMap, type SeedColorRoot, } from "./shared/bus-events.js";
66
69
  export * from "./shared/events.js";
67
70
  export { getImageDetails, releasePreviewUrl, type EspalierUploadImage, type ImageDetailsOptions, type SelectedUploadImage, type ExistingUploadImage, type ExistingImage, type ResponsiveImageUrl, type UploadCallbacks, type UploadEventDetail, } from "./image-upload/image-helpers.js";
68
71
  export { calculatePhotoLayout, type LayoutImage, type PhotoRow, } from "./shared/justified-layout.js";
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export*from"./box/esp-box.js";export*from"./action-menu/esp-action-menu.js";export*from"./action-menu/esp-action-menu-item.js";export*from"./avatar/esp-avatar.js";export*from"./avatar/esp-profile-chip.js";export*from"./badge/esp-badge.js";export*from"./checkbox/esp-checkbox.js";export*from"./checkbox/esp-checkbox-group.js";export*from"./data-cell/esp-data-cell.js";export*from"./radio-button/esp-radio-button.js";export*from"./radio-button/esp-radio-button-group.js";export*from"./repeater/esp-repeater.js";export*from"./breadcrumbs/esp-breadcrumbs.js";export*from"./button/esp-button.js";export*from"./button/esp-button-group.js";export*from"./color-picker/esp-color-picker.js";export*from"./flyout/esp-flyout.js";export*from"./form/esp-form.js";export*from"./form-item/esp-form-item.js";export*from"./header/index.js";export*from"./page/esp-page.js";export*from"./popover/esp-popover.js";export*from"./dialog/esp-dialog.js";export*from"./empty-state/esp-empty-state.js";export*from"./image-upload/esp-image-upload.js";export*from"./file-upload/esp-file-upload.js";export*from"./font-picker/esp-font-picker.js";export*from"./focus-picker/esp-focus-picker.js";export*from"./footer/index.js";export*from"./grid/esp-grid.js";export*from"./image/esp-image.js";export*from"./image/image-focus.js";export*from"./lightbox/esp-lightbox.js";export*from"./info/esp-info.js";export*from"./input/esp-input.js";export*from"./textarea/esp-textarea.js";export*from"./pickers/esp-pick-one.js";export*from"./pickers/esp-pick-some.js";export*from"./pickers/esp-picker-menu.js";export*from"./pickers/esp-picker-item.js";export*from"./root/esp-root.js";export*from"./menu/index.js";export*from"./burger/esp-burger.js";export*from"./date-picker/esp-date-picker.js";export*from"./details/esp-details.js";export*from"./details/esp-details-group.js";export*from"./tabs/esp-tab.js";export*from"./tabs/esp-tab-group.js";export*from"./slider/esp-slider.js";export*from"./switch/esp-switch.js";export*from"./toaster/esp-toaster.js";export*from"./tooltip/esp-tooltip.js";export*from"./progress/esp-progress.js";export*from"./search/esp-search.js";export*from"./status-indicator/esp-status-indicator.js";export*from"./tree/esp-tree.js";export*from"./tree/esp-tree-item.js";import{DEFAULT_ICON_SPRITE_URL as po,DEFAULT_ICON_VIEW_BOX as fo,INTENT_VARIANTS as xo,normalizeIntentVariant as ao,getIconHref as lo,getIconHrefForHost as so,getIconSpriteUrl as To}from"./shared/intent-values.js";import{EspalierElementBase as io}from"./shared/esp-element-base.js";import{VALIDITY_CHANGED_EVENT as Io}from"./shared/validation.js";import{FormFieldController as ho}from"./shared/form-field-controller.js";import{traverseToClosest as Fo}from"./shared/utilities.js";import{showToast as Ao}from"./shared/toast-events.js";import{showFlyout as No,closeFlyout as uo}from"./shared/flyout-events.js";import{getEspBus as Ho}from"./shared/bus-events.js";export*from"./shared/events.js";import{getImageDetails as yo,releasePreviewUrl as Co}from"./image-upload/image-helpers.js";import{calculatePhotoLayout as Vo}from"./shared/justified-layout.js";import{encodeTheme as Bo,parseTheme as Po,mergePartials as vo,layerThemes as wo,buildTaprootLightTheme as Go,buildTaprootDarkTheme as Oo,NESTED_THEME_KEYS as Ro}from"./shared/theme.js";import{WEIGHT_LABELS as zo,extractWeights as Yo,normalizeWeight as Ko,bestAvailableWeight as Mo,extractFamily as Xo,getFallbackFont as jo}from"./shared/font-helpers.js";import{getGoogleFonts as Jo}from"./font-picker/esp-font-picker.js";export{po as DEFAULT_ICON_SPRITE_URL,fo as DEFAULT_ICON_VIEW_BOX,io as EspalierElementBase,ho as FormFieldController,xo as INTENT_VARIANTS,Ro as NESTED_THEME_KEYS,Io as VALIDITY_CHANGED_EVENT,zo as WEIGHT_LABELS,Mo as bestAvailableWeight,Oo as buildTaprootDarkTheme,Go as buildTaprootLightTheme,Vo as calculatePhotoLayout,uo as closeFlyout,Bo as encodeTheme,Xo as extractFamily,Yo as extractWeights,Ho as getEspBus,jo as getFallbackFont,Jo as getGoogleFonts,lo as getIconHref,so as getIconHrefForHost,To as getIconSpriteUrl,yo as getImageDetails,wo as layerThemes,vo as mergePartials,ao as normalizeIntentVariant,Ko as normalizeWeight,Po as parseTheme,Co as releasePreviewUrl,No as showFlyout,Ao as showToast,Fo as traverseToClosest};
1
+ export*from"./box/esp-box.js";export*from"./action-menu/esp-action-menu.js";export*from"./action-menu/esp-action-menu-item.js";export*from"./avatar/esp-avatar.js";export*from"./avatar/esp-profile-chip.js";export*from"./badge/esp-badge.js";export*from"./checkbox/esp-checkbox.js";export*from"./checkbox/esp-checkbox-group.js";export*from"./data-cell/esp-data-cell.js";export*from"./radio-button/esp-radio-button.js";export*from"./radio-button/esp-radio-button-group.js";export*from"./repeater/esp-repeater.js";export*from"./breadcrumbs/esp-breadcrumbs.js";export*from"./button/esp-button.js";export*from"./button/esp-button-group.js";export*from"./color-picker/esp-color-picker.js";export*from"./flyout/esp-flyout.js";export*from"./form/esp-form.js";export*from"./form-item/esp-form-item.js";export*from"./help/esp-help-button.js";export*from"./help/esp-help-provider.js";export*from"./header/index.js";export*from"./page/esp-page.js";export*from"./popover/esp-popover.js";export*from"./dialog/esp-dialog.js";export*from"./empty-state/esp-empty-state.js";export*from"./image-upload/esp-image-upload.js";export*from"./file-upload/esp-file-upload.js";export*from"./font-picker/esp-font-picker.js";export*from"./focus-picker/esp-focus-picker.js";export*from"./footer/index.js";export*from"./grid/esp-grid.js";export*from"./image/esp-image.js";export*from"./image/image-focus.js";export*from"./lightbox/esp-lightbox.js";export*from"./info/esp-info.js";export*from"./input/esp-input.js";export*from"./textarea/esp-textarea.js";export*from"./pickers/esp-pick-one.js";export*from"./pickers/esp-pick-some.js";export*from"./pickers/esp-picker-menu.js";export*from"./pickers/esp-picker-item.js";export*from"./root/esp-root.js";export*from"./menu/index.js";export*from"./burger/esp-burger.js";export*from"./date-picker/esp-date-picker.js";export*from"./details/esp-details.js";export*from"./details/esp-details-group.js";export*from"./tabs/esp-tab.js";export*from"./tabs/esp-tab-group.js";export*from"./slider/esp-slider.js";export*from"./switch/esp-switch.js";export*from"./toaster/esp-toaster.js";export*from"./tooltip/esp-tooltip.js";export*from"./progress/esp-progress.js";export*from"./search/esp-search.js";export*from"./status-indicator/esp-status-indicator.js";export*from"./tree/esp-tree.js";export*from"./tree/esp-tree-item.js";import{DEFAULT_ICON_SPRITE_URL as xo,DEFAULT_ICON_VIEW_BOX as ao,INTENT_VARIANTS as lo,normalizeIntentVariant as so,getIconHref as To,getIconHrefForHost as Eo,getIconSpriteUrl as io}from"./shared/intent-values.js";import{EspalierElementBase as Io}from"./shared/esp-element-base.js";import{VALIDITY_CHANGED_EVENT as ho}from"./shared/validation.js";import{FormFieldController as Fo}from"./shared/form-field-controller.js";import{traverseToClosest as uo}from"./shared/utilities.js";import{showToast as Lo}from"./shared/toast-events.js";import{showFlyout as Do,closeFlyout as Ho}from"./shared/flyout-events.js";import{requestHelp as bo}from"./shared/help-events.js";import{getEspBus as Co}from"./shared/bus-events.js";export*from"./shared/events.js";import{getImageDetails as Wo,releasePreviewUrl as Bo}from"./image-upload/image-helpers.js";import{calculatePhotoLayout as vo}from"./shared/justified-layout.js";import{encodeTheme as Go,parseTheme as Oo,mergePartials as Ro,layerThemes as ko,buildTaprootLightTheme as zo,buildTaprootDarkTheme as Yo,NESTED_THEME_KEYS as qo}from"./shared/theme.js";import{WEIGHT_LABELS as Mo,extractWeights as Xo,normalizeWeight as jo,bestAvailableWeight as Jo,extractFamily as Qo,getFallbackFont as Zo}from"./shared/font-helpers.js";import{getGoogleFonts as or}from"./font-picker/esp-font-picker.js";export{xo as DEFAULT_ICON_SPRITE_URL,ao as DEFAULT_ICON_VIEW_BOX,Io as EspalierElementBase,Fo as FormFieldController,lo as INTENT_VARIANTS,qo as NESTED_THEME_KEYS,ho as VALIDITY_CHANGED_EVENT,Mo as WEIGHT_LABELS,Jo as bestAvailableWeight,Yo as buildTaprootDarkTheme,zo as buildTaprootLightTheme,vo as calculatePhotoLayout,Ho as closeFlyout,Go as encodeTheme,Qo as extractFamily,Xo as extractWeights,Co as getEspBus,Zo as getFallbackFont,or as getGoogleFonts,To as getIconHref,Eo as getIconHrefForHost,io as getIconSpriteUrl,Wo as getImageDetails,ko as layerThemes,Ro as mergePartials,so as normalizeIntentVariant,jo as normalizeWeight,Oo as parseTheme,Bo as releasePreviewUrl,bo as requestHelp,Do as showFlyout,Lo as showToast,uo as traverseToClosest};
@@ -19,14 +19,14 @@ export interface SearchResult {
19
19
  * etc.) by listening for `search-requested` events and calling
20
20
  * `setResults()` with the matches.
21
21
  *
22
- * @fires search-requested The user changed the query input. Fires on
22
+ * @fires search-requested - The user changed the query input. Fires on
23
23
  * every keystroke (not debounced) so consumers can track the latest
24
24
  * query and discard stale async results. Consumers that need debouncing
25
25
  * should apply it themselves (e.g. Pagefind's `debouncedSearch()`).
26
26
  * `detail: { query: string }`
27
- * @fires result-selected The user chose a result.
27
+ * @fires result-selected - The user chose a result.
28
28
  * `detail: { url: string }`
29
- * @fires search-closed The overlay was closed (Escape, backdrop click,
29
+ * @fires search-closed - The overlay was closed (Escape, backdrop click,
30
30
  * or result selection). Consumers should use this to cancel in-flight
31
31
  * searches or discard pending results.
32
32
  * `detail: {}`
@@ -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};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taprootio/espalier",
3
- "version": "2.8.0",
3
+ "version": "2.9.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"