@taprootio/espalier 2.4.0 → 2.6.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.
- package/CHANGELOG.md +22 -0
- package/custom-elements.json +20111 -17330
- package/dist/footer/esp-footer-column.d.ts +47 -0
- package/dist/footer/esp-footer-column.js +8 -0
- package/dist/footer/esp-footer.d.ts +67 -6
- package/dist/footer/esp-footer.js +22 -11
- package/dist/footer/index.d.ts +1 -0
- package/dist/footer/index.js +1 -1
- package/dist/header/esp-header.d.ts +59 -6
- package/dist/header/esp-header.js +13 -14
- package/dist/root/esp-root.d.ts +39 -1
- package/dist/root/esp-root.js +2 -2
- package/dist/shared/bus-events.d.ts +12 -5
- package/dist/shared/configured-brand.js +4 -4
- package/dist/shared/esp-element-base.js +2 -2
- package/dist/shared/events.d.ts +7 -0
- package/dist/shared/events.js +1 -1
- package/dist/shared/root-event-subscription.js +1 -0
- package/espalier.token-manifest.json +1 -1
- package/package.json +5 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { EspalierElementBase } from "../shared/esp-element-base.js";
|
|
2
|
+
/**
|
|
3
|
+
* A layout-only column for creator-controlled link-group stacking inside
|
|
4
|
+
* [`<esp-footer>`](/components/footer/).
|
|
5
|
+
*
|
|
6
|
+
* Use bare `esp-footer-link-group` children when groups should auto-flow
|
|
7
|
+
* independently through the footer grid. Wrap ordered groups in
|
|
8
|
+
* `esp-footer-column` when they must remain together as one grid item:
|
|
9
|
+
*
|
|
10
|
+
* ```html
|
|
11
|
+
* <esp-footer columns="3">
|
|
12
|
+
* <esp-footer-column>
|
|
13
|
+
* <esp-footer-link-group heading="Product">
|
|
14
|
+
* <a href="/features">Features</a>
|
|
15
|
+
* <a href="/pricing">Pricing</a>
|
|
16
|
+
* </esp-footer-link-group>
|
|
17
|
+
* <esp-footer-link-group heading="Company">
|
|
18
|
+
* <a href="/about">About</a>
|
|
19
|
+
* <a href="/careers">Careers</a>
|
|
20
|
+
* </esp-footer-link-group>
|
|
21
|
+
* </esp-footer-column>
|
|
22
|
+
* </esp-footer>
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* The component adds no heading, landmark, or chrome. It preserves the
|
|
26
|
+
* slotted order of its child groups and uses `--esp-footer-row-gap`, defaulting
|
|
27
|
+
* to `var(--esp-size-big)`, for its vertical rhythm.
|
|
28
|
+
*
|
|
29
|
+
* @customElement esp-footer-column
|
|
30
|
+
* @slot - Ordered `esp-footer-link-group` children belonging to this column.
|
|
31
|
+
* @cssprop --esp-footer-row-gap - Vertical gap between footer grid rows and
|
|
32
|
+
* link groups stacked in `esp-footer-column`. Defaults to `var(--esp-size-big)`.
|
|
33
|
+
* @docPageTitle Footer Column
|
|
34
|
+
* @docUrl /components/footer/column
|
|
35
|
+
* @menuGroup Structure
|
|
36
|
+
* @menuLabel Footer Column
|
|
37
|
+
* @menuIcon columns
|
|
38
|
+
*/
|
|
39
|
+
export declare class EspalierFooterColumn extends EspalierElementBase {
|
|
40
|
+
protected render(): import("lit-html").TemplateResult<1>;
|
|
41
|
+
static styles: import("lit").CSSResult[];
|
|
42
|
+
}
|
|
43
|
+
declare global {
|
|
44
|
+
interface HTMLElementTagNameMap {
|
|
45
|
+
"esp-footer-column": EspalierFooterColumn;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
var m=function(r,o,t,l){var n=arguments.length,e=n<3?o:l===null?l=Object.getOwnPropertyDescriptor(o,t):l,s;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")e=Reflect.decorate(r,o,t,l);else for(var f=r.length-1;f>=0;f--)(s=r[f])&&(e=(n<3?s(e):n>3?s(o,t,e):s(o,t))||e);return n>3&&e&&Object.defineProperty(o,t,e),e};import{css as c,html as a}from"lit";import{customElement as u}from"lit/decorators.js";import{EspalierElementBase as p}from"../shared/esp-element-base.js";let i=class extends p{render(){return a`<slot></slot>`}};i.styles=[...p.styles,c`
|
|
2
|
+
:host {
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-direction: column;
|
|
5
|
+
gap: var(--esp-footer-row-gap, var(--esp-size-big));
|
|
6
|
+
min-inline-size: 0;
|
|
7
|
+
}
|
|
8
|
+
`],i=m([u("esp-footer-column")],i);export{i as EspalierFooterColumn};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type PropertyValues } from "lit";
|
|
2
2
|
import { EspalierElementBase } from "../shared/esp-element-base.js";
|
|
3
|
+
import "./esp-footer-column.js";
|
|
3
4
|
import "./esp-footer-link-group.js";
|
|
4
5
|
export type FooterColumns = "auto" | "1" | "2" | "3" | "4" | "5" | "6";
|
|
5
6
|
/**
|
|
@@ -8,16 +9,19 @@ export type FooterColumns = "auto" | "1" | "2" | "3" | "4" | "5" | "6";
|
|
|
8
9
|
* Its background remains full-bleed while its content automatically aligns
|
|
9
10
|
* to the surface of a containing [`<esp-page>`](/components/page/).
|
|
10
11
|
*
|
|
11
|
-
* Use [`<esp-footer-link-group>`](/components/footer/link-group/) children
|
|
12
|
-
*
|
|
13
|
-
* columns when they no longer fit.
|
|
12
|
+
* Use bare [`<esp-footer-link-group>`](/components/footer/link-group/) children
|
|
13
|
+
* when navigation groups should auto-flow independently through the grid.
|
|
14
|
+
* `columns` is a maximum: the grid always drops columns when they no longer fit.
|
|
14
15
|
*
|
|
15
16
|
* ```html
|
|
16
17
|
* <esp-footer
|
|
17
|
-
* brand-logo="/assets/
|
|
18
|
+
* light-brand-logo="/assets/logo-light.svg"
|
|
19
|
+
* dark-brand-logo="/assets/logo-dark.svg"
|
|
18
20
|
* brand-text="Espalier"
|
|
19
21
|
* brand-href="/"
|
|
20
22
|
* brand-alt="Espalier logo"
|
|
23
|
+
* light-brand-color="oklch(0.35 0.12 216)"
|
|
24
|
+
* dark-brand-color="oklch(0.9 0.08 216)"
|
|
21
25
|
* columns="3">
|
|
22
26
|
* <esp-footer-link-group heading="Explore">
|
|
23
27
|
* <a href="/components">Components</a>
|
|
@@ -32,6 +36,33 @@ export type FooterColumns = "auto" | "1" | "2" | "3" | "4" | "5" | "6";
|
|
|
32
36
|
* </esp-footer>
|
|
33
37
|
* ```
|
|
34
38
|
*
|
|
39
|
+
* For creator-controlled columns, wrap each ordered stack of groups in
|
|
40
|
+
* [`<esp-footer-column>`](/components/footer/column/). Each wrapper is one grid
|
|
41
|
+
* item at wide sizes; below the footer's compact container breakpoint, the
|
|
42
|
+
* columns become full-width rows in their slotted order. `columns` remains the
|
|
43
|
+
* maximum number of grid items per row:
|
|
44
|
+
*
|
|
45
|
+
* ```html
|
|
46
|
+
* <esp-footer columns="3">
|
|
47
|
+
* <esp-footer-column>
|
|
48
|
+
* <esp-footer-link-group heading="Product">
|
|
49
|
+
* <a href="/features">Features</a>
|
|
50
|
+
* <a href="/pricing">Pricing</a>
|
|
51
|
+
* </esp-footer-link-group>
|
|
52
|
+
* <esp-footer-link-group heading="Company">
|
|
53
|
+
* <a href="/about">About</a>
|
|
54
|
+
* <a href="/careers">Careers</a>
|
|
55
|
+
* </esp-footer-link-group>
|
|
56
|
+
* </esp-footer-column>
|
|
57
|
+
* <esp-footer-column>
|
|
58
|
+
* <esp-footer-link-group heading="Resources">
|
|
59
|
+
* <a href="/docs">Documentation</a>
|
|
60
|
+
* <a href="/support">Support</a>
|
|
61
|
+
* </esp-footer-link-group>
|
|
62
|
+
* </esp-footer-column>
|
|
63
|
+
* </esp-footer>
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
35
66
|
* A slotted brand replaces configured branding, while the media slot accepts
|
|
36
67
|
* a responsive `picture`, native image, or `esp-image`. Media spans the
|
|
37
68
|
* surface-aligned content well; use the decorative background layer for
|
|
@@ -109,7 +140,8 @@ export type FooterColumns = "auto" | "1" | "2" | "3" | "4" | "5" | "6";
|
|
|
109
140
|
* preferably responsive `<picture>` markup or an `esp-image` with appropriate
|
|
110
141
|
* alternative text.
|
|
111
142
|
* @slot brand - Custom brand identity. Overrides configured brand properties.
|
|
112
|
-
* @slot - `esp-footer-link-group`
|
|
143
|
+
* @slot - Bare `esp-footer-link-group` children for automatic flow, or
|
|
144
|
+
* `esp-footer-column` children for creator-controlled group stacks.
|
|
113
145
|
* @slot aside - Supporting content such as a newsletter, call to action,
|
|
114
146
|
* contact details, social links, or locale controls.
|
|
115
147
|
* @slot bottom - Copyright, legal/utility links, attribution, accessibility
|
|
@@ -134,7 +166,8 @@ export type FooterColumns = "auto" | "1" | "2" | "3" | "4" | "5" | "6";
|
|
|
134
166
|
* @cssprop --esp-footer-padding-inline - Content inline padding. Defaults to `var(--esp-size-padding-page)`.
|
|
135
167
|
* @cssprop --esp-footer-section-gap - Gap between media, primary, and bottom regions. Defaults to `var(--esp-size-big)`.
|
|
136
168
|
* @cssprop --esp-footer-column-gap - Gap between link-group columns and major primary regions. Defaults to `var(--esp-size-padding-page)`.
|
|
137
|
-
* @cssprop --esp-footer-row-gap -
|
|
169
|
+
* @cssprop --esp-footer-row-gap - Vertical gap between footer grid rows and
|
|
170
|
+
* link groups stacked in `esp-footer-column`. Defaults to `var(--esp-size-big)`.
|
|
138
171
|
* @cssprop --esp-footer-link-group-min-width - Intrinsic minimum link-group column width. Defaults to `10rem`.
|
|
139
172
|
* @cssprop --esp-footer-link-group-gap - Space between a link-group heading and its links. Defaults to `var(--esp-size-small)`.
|
|
140
173
|
* @cssprop --esp-footer-brand-logo-size - Configured logo block size. Defaults to `3rem`.
|
|
@@ -158,10 +191,38 @@ export declare class EspalierFooter extends EspalierElementBase {
|
|
|
158
191
|
brandText: string;
|
|
159
192
|
/** Configured brand logo URL used when the `brand` slot is empty. */
|
|
160
193
|
brandLogo: string;
|
|
194
|
+
/**
|
|
195
|
+
* Configured brand logo URL used in light scheme. A non-empty value
|
|
196
|
+
* overrides `brand-logo`; an empty value falls back to it.
|
|
197
|
+
*/
|
|
198
|
+
lightBrandLogo: string;
|
|
199
|
+
/**
|
|
200
|
+
* Configured brand logo URL used in dark scheme. A non-empty value
|
|
201
|
+
* overrides `brand-logo`; an empty value falls back to it.
|
|
202
|
+
*/
|
|
203
|
+
darkBrandLogo: string;
|
|
161
204
|
/** Optional configured-brand link target. */
|
|
162
205
|
brandHref: string;
|
|
163
206
|
/** Accessible text for a configured logo. */
|
|
164
207
|
brandAlt: string;
|
|
208
|
+
/**
|
|
209
|
+
* Optional configured brand color. Scheme-specific colors override it;
|
|
210
|
+
* when all configured colors are empty, `--esp-footer-heading-color`
|
|
211
|
+
* remains the fallback.
|
|
212
|
+
*/
|
|
213
|
+
brandColor: string;
|
|
214
|
+
/**
|
|
215
|
+
* Configured brand color used in light scheme. A non-empty value
|
|
216
|
+
* overrides `brand-color`; an empty value falls back to it and then
|
|
217
|
+
* `--esp-footer-heading-color`.
|
|
218
|
+
*/
|
|
219
|
+
lightBrandColor: string;
|
|
220
|
+
/**
|
|
221
|
+
* Configured brand color used in dark scheme. A non-empty value
|
|
222
|
+
* overrides `brand-color`; an empty value falls back to it and then
|
|
223
|
+
* `--esp-footer-heading-color`.
|
|
224
|
+
*/
|
|
225
|
+
darkBrandColor: string;
|
|
165
226
|
/**
|
|
166
227
|
* Maximum number of link-group columns. Numeric values cap the wide layout;
|
|
167
228
|
* the intrinsic grid still renders fewer columns when space is limited.
|
|
@@ -1,16 +1,20 @@
|
|
|
1
|
-
var
|
|
2
|
-
<footer
|
|
1
|
+
var t=function(i,o,e,a){var s=arguments.length,d=s<3?o:a===null?a=Object.getOwnPropertyDescriptor(o,e):a,l;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")d=Reflect.decorate(i,o,e,a);else for(var p=i.length-1;p>=0;p--)(l=i[p])&&(d=(s<3?l(d):s>3?l(o,e,d):l(o,e))||d);return s>3&&d&&Object.defineProperty(o,e,d),d};import{css as f,html as v,nothing as y}from"lit";import{customElement as x,property as n,state as C}from"lit/decorators.js";import{classMap as k}from"lit/directives/class-map.js";import{styleMap as _}from"lit/directives/style-map.js";import{renderConfiguredBrand as z,resolveConfiguredBrand as B}from"../shared/configured-brand.js";import{EspalierElementBase as g}from"../shared/esp-element-base.js";import{slotHasContent as c}from"../shared/slot-content.js";import"./esp-footer-column.js";import"./esp-footer-link-group.js";const m=new Set(["auto","1","2","3","4","5","6"]),h={media:!1,brand:!1,groups:!1,aside:!1,bottom:!1},S={fromAttribute(i){return i&&m.has(i)?i:"auto"},toAttribute(i){return m.has(i)?i:"auto"}};let r=class extends g{constructor(){super(...arguments),this.slotContent={...h},this.brandText="",this.brandLogo="",this.lightBrandLogo="",this.darkBrandLogo="",this.brandHref="",this.brandAlt="",this.brandColor="",this.lightBrandColor="",this.darkBrandColor="",this.columns="auto",this.fullBleedContent=!1,this.landmarkLabel="",this.handleSlotChange=o=>{if(!(o.target instanceof HTMLSlotElement))return;const e=this.slotRegion(o.target);if(!e)return;const a=c(o.target);this.slotContent[e]!==a&&(this.slotContent={...this.slotContent,[e]:a})}}willUpdate(o){super.willUpdate(o),o.has("columns")&&!m.has(this.columns)&&(this.columns="auto")}slotRegion(o){return o.name?["media","brand","aside","bottom"].includes(o.name)?o.name:null:"groups"}firstUpdated(o){super.firstUpdated(o);const e={...h};for(const a of this.shadowRoot?.querySelectorAll("slot")??[]){const s=this.slotRegion(a);s&&(e[s]=c(a))}Object.entries(e).some(([a,s])=>this.slotContent[a]!==s)&&(this.slotContent=e)}render(){const{media:o,brand:e,groups:a,aside:s,bottom:d}=this.slotContent,l=B({scheme:this.scheme,brandLogo:this.brandLogo,brandColor:this.brandColor,lightBrandLogo:this.lightBrandLogo,darkBrandLogo:this.darkBrandLogo,lightBrandColor:this.lightBrandColor,darkBrandColor:this.darkBrandColor}),p=e||!!(l.brandLogo||this.brandText),u=p||a||s,b={primary:!0,"has-brand":p,"has-groups":a,"has-aside":s};return v`
|
|
2
|
+
<footer
|
|
3
|
+
part="footer"
|
|
4
|
+
aria-label=${this.landmarkLabel||y}
|
|
5
|
+
style=${_({"--_esp-footer-brand-color":l.brandColor||null})}
|
|
6
|
+
>
|
|
3
7
|
<div part="background" class="background" aria-hidden="true"></div>
|
|
4
8
|
<div part="content" class="content-frame">
|
|
5
9
|
<div class="content">
|
|
6
|
-
<div part="media" class="media" ?hidden=${!
|
|
10
|
+
<div part="media" class="media" ?hidden=${!o}>
|
|
7
11
|
<slot name="media" @slotchange=${this.handleSlotChange}></slot>
|
|
8
12
|
</div>
|
|
9
13
|
|
|
10
|
-
<div part="primary" class=${
|
|
11
|
-
<div part="brand" class="brand" ?hidden=${!
|
|
14
|
+
<div part="primary" class=${k(b)} ?hidden=${!u}>
|
|
15
|
+
<div part="brand" class="brand" ?hidden=${!p}>
|
|
12
16
|
<slot name="brand" @slotchange=${this.handleSlotChange}
|
|
13
|
-
>${z({brandLogo:
|
|
17
|
+
>${z({brandLogo:l.brandLogo,brandText:this.brandText,brandHref:this.brandHref,brandAlt:this.brandAlt})}</slot
|
|
14
18
|
>
|
|
15
19
|
</div>
|
|
16
20
|
|
|
@@ -18,18 +22,18 @@ var i=function(s,e,r,a){var o=arguments.length,n=o<3?e:a===null?a=Object.getOwnP
|
|
|
18
22
|
<slot @slotchange=${this.handleSlotChange}></slot>
|
|
19
23
|
</div>
|
|
20
24
|
|
|
21
|
-
<div part="aside" class="aside" ?hidden=${!
|
|
25
|
+
<div part="aside" class="aside" ?hidden=${!s}>
|
|
22
26
|
<slot name="aside" @slotchange=${this.handleSlotChange}></slot>
|
|
23
27
|
</div>
|
|
24
28
|
</div>
|
|
25
29
|
|
|
26
|
-
<div part="bottom" class="bottom" ?hidden=${!
|
|
30
|
+
<div part="bottom" class="bottom" ?hidden=${!d}>
|
|
27
31
|
<slot name="bottom" @slotchange=${this.handleSlotChange}></slot>
|
|
28
32
|
</div>
|
|
29
33
|
</div>
|
|
30
34
|
</div>
|
|
31
35
|
</footer>
|
|
32
|
-
`}};
|
|
36
|
+
`}};r.styles=[...g.styles,f`
|
|
33
37
|
:host {
|
|
34
38
|
display: block;
|
|
35
39
|
min-inline-size: 0;
|
|
@@ -216,7 +220,10 @@ var i=function(s,e,r,a){var o=arguments.length,n=o<3?e:a===null?a=Object.getOwnP
|
|
|
216
220
|
align-items: center;
|
|
217
221
|
gap: var(--esp-size-small);
|
|
218
222
|
max-inline-size: 100%;
|
|
219
|
-
color: var(
|
|
223
|
+
color: var(
|
|
224
|
+
--_esp-footer-brand-color,
|
|
225
|
+
var(--esp-footer-heading-color, var(--esp-color-headings))
|
|
226
|
+
);
|
|
220
227
|
font-family: var(--esp-font-brand, var(--esp-font-headings));
|
|
221
228
|
font-size: var(--esp-type-medium);
|
|
222
229
|
font-weight: var(--esp-font-weight-brand, var(--esp-font-weight-headings));
|
|
@@ -250,6 +257,10 @@ var i=function(s,e,r,a){var o=arguments.length,n=o<3?e:a===null?a=Object.getOwnP
|
|
|
250
257
|
}
|
|
251
258
|
|
|
252
259
|
@container esp-footer (max-width: 48rem) {
|
|
260
|
+
.groups ::slotted(esp-footer-column) {
|
|
261
|
+
grid-column: 1 / -1;
|
|
262
|
+
}
|
|
263
|
+
|
|
253
264
|
.primary,
|
|
254
265
|
.primary.has-brand.has-groups:not(.has-aside),
|
|
255
266
|
.primary.has-groups.has-aside:not(.has-brand),
|
|
@@ -297,4 +308,4 @@ var i=function(s,e,r,a){var o=arguments.length,n=o<3?e:a===null?a=Object.getOwnP
|
|
|
297
308
|
grid-template-areas: "aside";
|
|
298
309
|
}
|
|
299
310
|
}
|
|
300
|
-
`],
|
|
311
|
+
`],t([C()],r.prototype,"slotContent",void 0),t([n({attribute:"brand-text",type:String})],r.prototype,"brandText",void 0),t([n({attribute:"brand-logo",type:String})],r.prototype,"brandLogo",void 0),t([n({attribute:"light-brand-logo",type:String})],r.prototype,"lightBrandLogo",void 0),t([n({attribute:"dark-brand-logo",type:String})],r.prototype,"darkBrandLogo",void 0),t([n({attribute:"brand-href",type:String})],r.prototype,"brandHref",void 0),t([n({attribute:"brand-alt",type:String})],r.prototype,"brandAlt",void 0),t([n({attribute:"brand-color",type:String})],r.prototype,"brandColor",void 0),t([n({attribute:"light-brand-color",type:String})],r.prototype,"lightBrandColor",void 0),t([n({attribute:"dark-brand-color",type:String})],r.prototype,"darkBrandColor",void 0),t([n({converter:S,reflect:!0})],r.prototype,"columns",void 0),t([n({attribute:"full-bleed-content",type:Boolean,reflect:!0})],r.prototype,"fullBleedContent",void 0),t([n({attribute:"landmark-label",type:String})],r.prototype,"landmarkLabel",void 0),r=t([x("esp-footer")],r);export{r as EspalierFooter};
|
package/dist/footer/index.d.ts
CHANGED
package/dist/footer/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export*from"./esp-footer-link-group.js";export*from"./esp-footer.js";
|
|
1
|
+
export*from"./esp-footer-column.js";export*from"./esp-footer-link-group.js";export*from"./esp-footer.js";
|
|
@@ -2,11 +2,19 @@ import { type PropertyValues } from "lit";
|
|
|
2
2
|
import "../burger/esp-burger.js";
|
|
3
3
|
import { type EspalierBurger } from "../burger/esp-burger.js";
|
|
4
4
|
import { EspalierElementBase } from "../shared/esp-element-base.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
declare const HEADER_LAYOUT_VALUES: readonly ["standard", "centered-brand", "extended", "extended-centered", "minimal"];
|
|
6
|
+
declare const HEADER_BRAND_ALIGN_VALUES: readonly ["start", "center", "end"];
|
|
7
|
+
declare const HEADER_BRAND_WRAP_VALUES: readonly ["truncate", "wrap", "nowrap"];
|
|
8
|
+
declare const HEADER_MENU_DISPLAY_VALUES: readonly ["auto", "inline", "drawer"];
|
|
9
|
+
declare const HEADER_THEME_TOGGLE_VALUES: readonly ["hidden", "visible"];
|
|
10
|
+
export type HeaderLayout = (typeof HEADER_LAYOUT_VALUES)[number];
|
|
11
|
+
export type HeaderBrandAlign = (typeof HEADER_BRAND_ALIGN_VALUES)[number];
|
|
12
|
+
export type HeaderBrandWrap = (typeof HEADER_BRAND_WRAP_VALUES)[number];
|
|
13
|
+
export type HeaderMenuDisplay = (typeof HEADER_MENU_DISPLAY_VALUES)[number];
|
|
14
|
+
export type HeaderThemeToggle = (typeof HEADER_THEME_TOGGLE_VALUES)[number];
|
|
15
|
+
export interface EspThemeToggleEventDetail {
|
|
16
|
+
scheme: "light" | "dark";
|
|
17
|
+
}
|
|
10
18
|
/**
|
|
11
19
|
* Used to build the top navigation for a site. An empty header
|
|
12
20
|
* displays a bar. It has three slots for populating header content:
|
|
@@ -104,10 +112,13 @@ type HeaderThemeToggle = "hidden" | "visible";
|
|
|
104
112
|
* <esp-header
|
|
105
113
|
* slot="header"
|
|
106
114
|
* layout="centered-brand"
|
|
107
|
-
* brand-logo="/assets/
|
|
115
|
+
* light-brand-logo="/assets/logo-light.svg"
|
|
116
|
+
* dark-brand-logo="/assets/logo-dark.svg"
|
|
108
117
|
* brand-text="Espalier"
|
|
109
118
|
* brand-href="/"
|
|
110
119
|
* brand-alt="Espalier logo"
|
|
120
|
+
* light-brand-color="oklch(0.35 0.12 216)"
|
|
121
|
+
* dark-brand-color="oklch(0.9 0.08 216)"
|
|
111
122
|
* brand-align="center"
|
|
112
123
|
* menu-display="auto"
|
|
113
124
|
* theme-toggle="visible"
|
|
@@ -137,6 +148,19 @@ type HeaderThemeToggle = "hidden" | "visible";
|
|
|
137
148
|
* </esp-header>
|
|
138
149
|
* ```
|
|
139
150
|
*
|
|
151
|
+
* The built-in theme toggle emits `esp-theme-toggle` only for a visitor
|
|
152
|
+
* activation, so a host can persist explicit choices without treating
|
|
153
|
+
* automatic scheme resolution as intent:
|
|
154
|
+
*
|
|
155
|
+
* ```js
|
|
156
|
+
* const root = document.querySelector("esp-root");
|
|
157
|
+
* root.addEventListener("esp-theme-toggle", (event) => {
|
|
158
|
+
* localStorage.setItem("scheme", event.detail.scheme);
|
|
159
|
+
* });
|
|
160
|
+
* ```
|
|
161
|
+
*
|
|
162
|
+
* @event {CustomEvent<EspThemeToggleEventDetail>} esp-theme-toggle - Fired after the built-in theme toggle changes the nearest `esp-root`. Bubbles and crosses the shadow boundary.
|
|
163
|
+
*
|
|
140
164
|
* @cssprop --esp-header-background - The background color of the header bar. Defaults to `var(--esp-color-layer-2)`.
|
|
141
165
|
* @cssprop --esp-header-border-width - The border width of the header. Defaults to `0 0 1px 0`.
|
|
142
166
|
* @cssprop --esp-header-border - The border style of the header. Defaults to `solid var(--esp-color-border)`.
|
|
@@ -240,6 +264,8 @@ export declare class EspalierHeader extends EspalierElementBase {
|
|
|
240
264
|
* picker.addEventListener("value-changed", applyLayout);
|
|
241
265
|
* </script>
|
|
242
266
|
* ```
|
|
267
|
+
*
|
|
268
|
+
* @default "standard"
|
|
243
269
|
*/
|
|
244
270
|
layout: HeaderLayout;
|
|
245
271
|
/**
|
|
@@ -250,6 +276,16 @@ export declare class EspalierHeader extends EspalierElementBase {
|
|
|
250
276
|
* Configured brand logo URL used when the `brand` slot is empty.
|
|
251
277
|
*/
|
|
252
278
|
brandLogo: string;
|
|
279
|
+
/**
|
|
280
|
+
* Configured brand logo URL used in light scheme. A non-empty value
|
|
281
|
+
* overrides `brand-logo`; an empty value falls back to it.
|
|
282
|
+
*/
|
|
283
|
+
lightBrandLogo: string;
|
|
284
|
+
/**
|
|
285
|
+
* Configured brand logo URL used in dark scheme. A non-empty value
|
|
286
|
+
* overrides `brand-logo`; an empty value falls back to it.
|
|
287
|
+
*/
|
|
288
|
+
darkBrandLogo: string;
|
|
253
289
|
/**
|
|
254
290
|
* Optional brand link used by configured logo/text branding.
|
|
255
291
|
*/
|
|
@@ -265,24 +301,40 @@ export declare class EspalierHeader extends EspalierElementBase {
|
|
|
265
301
|
* header override.
|
|
266
302
|
*/
|
|
267
303
|
brandColor: string;
|
|
304
|
+
/**
|
|
305
|
+
* Configured brand color used in light scheme. A non-empty value
|
|
306
|
+
* overrides `brand-color`; an empty value falls back to it and then
|
|
307
|
+
* `--esp-header-brand-color`.
|
|
308
|
+
*/
|
|
309
|
+
lightBrandColor: string;
|
|
310
|
+
/**
|
|
311
|
+
* Configured brand color used in dark scheme. A non-empty value
|
|
312
|
+
* overrides `brand-color`; an empty value falls back to it and then
|
|
313
|
+
* `--esp-header-brand-color`.
|
|
314
|
+
*/
|
|
315
|
+
darkBrandColor: string;
|
|
268
316
|
/**
|
|
269
317
|
* Horizontal placement of the brand content inside its grid area.
|
|
318
|
+
* @default "start"
|
|
270
319
|
*/
|
|
271
320
|
brandAlign: HeaderBrandAlign;
|
|
272
321
|
/**
|
|
273
322
|
* Long-brand behavior for configured brand text.
|
|
323
|
+
* @default "truncate"
|
|
274
324
|
*/
|
|
275
325
|
brandWrap: HeaderBrandWrap;
|
|
276
326
|
/**
|
|
277
327
|
* Navigation display intent. `auto` tries inline navigation before
|
|
278
328
|
* using a drawer, `drawer` always uses the burger/drawer pattern,
|
|
279
329
|
* and `inline` leaves overflow handling to the slotted menu.
|
|
330
|
+
* @default "auto"
|
|
280
331
|
*/
|
|
281
332
|
menuDisplay: HeaderMenuDisplay;
|
|
282
333
|
/**
|
|
283
334
|
* Whether the header renders its built-in light/dark toggle.
|
|
284
335
|
* Consumers can still provide their own control in the `buttons`
|
|
285
336
|
* slot when this is `hidden`.
|
|
337
|
+
* @default "hidden"
|
|
286
338
|
*/
|
|
287
339
|
themeToggle: HeaderThemeToggle;
|
|
288
340
|
/**
|
|
@@ -323,6 +375,7 @@ export declare class EspalierHeader extends EspalierElementBase {
|
|
|
323
375
|
* (e.g. when a drawer is closed via swipe).
|
|
324
376
|
*/
|
|
325
377
|
get burger(): EspalierBurger | undefined;
|
|
378
|
+
protected willUpdate(changedProperties: PropertyValues): void;
|
|
326
379
|
protected firstUpdated(props: PropertyValues): void;
|
|
327
380
|
protected updated(changedProperties: PropertyValues): void;
|
|
328
381
|
connectedCallback(): void;
|
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
var n=function(c,e,t,o){var s=arguments.length,i=s<3?e:o===null?o=Object.getOwnPropertyDescriptor(e,t):o,l;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")i=Reflect.decorate(c,e,t,o);else for(var d=c.length-1;d>=0;d--)(l=c[d])&&(i=(s<3?l(i):s>3?l(e,t,i):l(e,t))||i);return s>3&&i&&Object.defineProperty(e,t,i),i};import{css as x,html as g,nothing as b}from"lit";import{customElement as D,property as a,state as h}from"lit/decorators.js";import{classMap as M}from"lit/directives/class-map.js";import{styleMap as T}from"lit/directives/style-map.js";import"../burger/esp-burger.js";import{createRef as f,ref as v}from"lit/directives/ref.js";import{EspalierMenu as u}from"./esp-menu.js";import{renderConfiguredBrand as S}from"../shared/configured-brand.js";import{EspalierElementBase as w}from"../shared/esp-element-base.js";const C="(max-width: 36rem)";let r=class extends w{constructor(){super(...arguments),this.menuOpen=!1,this.menuTooLarge=!1,this.mobileMenu=!1,this.scrolled=!1,this.hiddenByScroll=!1,this.scrollProgress=0,this.showBurger=!1,this.layout="standard",this.brandText="",this.brandLogo="",this.brandHref="",this.brandAlt="",this.brandColor="",this.brandAlign="start",this.brandWrap="truncate",this.menuDisplay="auto",this.themeToggle="hidden",this.scrollBehavior="none",this.scrollThreshold=16,this.dockOffset=0,this.drawerTarget="",this.fullBleedContent=!1,this.menuContainer=f(),this.menuButton=f(),this.menu=void 0,this.externalDrawerTarget=null,this.lastScrollY=0,this.lastInlineMenuWidth=0,this.mobileMenuQuery=null,this.menuDrawerListenersAttached=!1,this.observingMenuContainer=!1,this.scrollListenersAttached=!1,this.scrollRafId=null,this.resizeObserver=new ResizeObserver(()=>this.syncMenuLayout()),this.menuChildObserver=null,this.updateScrollState=()=>{this.scrollRafId===null&&(this.scrollRafId=requestAnimationFrame(()=>{this.scrollRafId=null,this.runScrollStateUpdate()}))},this.handleMobileMenuQueryChange=e=>{this.mobileMenu=e.matches,this.syncMenuLayout(),this.syncExternalDrawerControl()},this.toggleTheme=()=>{const e=this.getThemeRoot();e&&(e.scheme=e.scheme==="dark"?"light":"dark")},this.handleMenuToggleClicked=()=>{if(this.menuOpen){this.closeNav();return}this.openNav()},this.onMenuDrawerOpened=()=>{this.menuOpen=!0,document.body.classList.add("menu-open"),this.burger&&(this.burger.menuOpen=!0)},this.onMenuDrawerClosed=()=>{this.menuOpen=!1,document.body.classList.remove("menu-open"),this.burger&&(this.burger.menuOpen=!1)}}syncMenuLayout(){if(!this.menuContainer.value||!this.menu||!this.menu.getWidth)return;const e=this.menuContainer.value.clientWidth,t=this.menu.getWidth();t>0&&(this.lastInlineMenuWidth=t);const o=this.lastInlineMenuWidth||t,s=this.getExternalDrawerTarget(),i=this.menuDisplay!=="inline",l=this.showBurger||this.menuDisplay==="drawer"||this.layout==="minimal";this.menuTooLarge=i&&this.menuDisplay==="auto"&&o>e,this.menu.sliding=i&&!s&&this.hasDrawerableContent()&&(this.menuTooLarge||l||this.mobileMenu)}menuHasContent(){return!!(this.menu??this.querySelector(':scope > [slot="menu"]'))?.querySelector("esp-menu-item, esp-menu-group")}hasDrawerableContent(){return this.showBurger||!!this.getExternalDrawerTarget()||this.menuHasContent()}getExternalDrawerTarget(){if(!this.drawerTarget)return null;const e=document.getElementById(this.drawerTarget);return e===this.menu?null:e&&"openDrawer"in e&&typeof e.openDrawer=="function"&&"closeDrawer"in e&&typeof e.closeDrawer=="function"?e:null}getBurgerFocusTarget(){const t=this.shadowRoot?.querySelector("esp-header-button.menu-toggle")?.shadowRoot?.querySelector("button"),o=this.menuButton.value?.shadowRoot?.querySelector("button");return t??o??null}getScrollTokens(){const e=this.scrollBehavior.split(/\s+/).map(t=>t.trim().toLowerCase()).filter(Boolean);return new Set(e.filter(t=>t!=="none"))}getEffectiveScrollThreshold(){return Number.isFinite(this.scrollThreshold)?Math.max(0,this.scrollThreshold):0}clearScrollState(){this.scrolled=!1,this.hiddenByScroll=!1,this.scrollProgress=0}runScrollStateUpdate(){const e=this.getScrollTokens();if(!e.size){this.clearScrollState();return}const t=Math.max(0,window.scrollY||document.documentElement.scrollTop||0),o=this.getEffectiveScrollThreshold(),s=Number.isFinite(this.dockOffset)?Math.max(0,this.dockOffset):o,i=t>(e.has("dock")?s:o),l=t>this.lastScrollY,d=e.has("reveal")&&l&&t>o&&!this.menuOpen;if(this.scrolled=i,this.hiddenByScroll=d,e.has("progress")){const p=document.documentElement,m=Math.max(1,p.scrollHeight-window.innerHeight);this.scrollProgress=Math.min(1,t/m)}else this.scrollProgress=0;this.lastScrollY=t}cancelScrollUpdate(){this.scrollRafId!==null&&(cancelAnimationFrame(this.scrollRafId),this.scrollRafId=null)}syncScrollListeners(){const e=this.getScrollTokens().size>0;if(e&&!this.scrollListenersAttached?(window.addEventListener("scroll",this.updateScrollState,{passive:!0}),window.addEventListener("resize",this.updateScrollState,{passive:!0}),this.scrollListenersAttached=!0):!e&&this.scrollListenersAttached&&this.removeScrollListeners(),e){this.updateScrollState();return}this.cancelScrollUpdate(),this.clearScrollState()}removeScrollListeners(){this.scrollListenersAttached&&(window.removeEventListener("scroll",this.updateScrollState),window.removeEventListener("resize",this.updateScrollState),this.scrollListenersAttached=!1)}addMenuDrawerListeners(){!this.menu||this.menuDrawerListenersAttached||(this.menu.addEventListener("drawer-opened",this.onMenuDrawerOpened),this.menu.addEventListener("drawer-closed",this.onMenuDrawerClosed),this.menuDrawerListenersAttached=!0)}removeMenuDrawerListeners(){!this.menu||!this.menuDrawerListenersAttached||(this.menu.removeEventListener("drawer-opened",this.onMenuDrawerOpened),this.menu.removeEventListener("drawer-closed",this.onMenuDrawerClosed),this.menuDrawerListenersAttached=!1)}observeMenuContainer(){!this.menuContainer.value||this.observingMenuContainer||(this.resizeObserver.observe(this.menuContainer.value),this.observingMenuContainer=!0)}disconnectResizeObserver(){this.observingMenuContainer&&(this.resizeObserver.disconnect(),this.observingMenuContainer=!1)}observeMenuChildren(){!this.menu||this.menuChildObserver||(this.menuChildObserver=new MutationObserver(()=>{this.syncMenuLayout(),this.requestUpdate()}),this.menuChildObserver.observe(this.menu,{childList:!0,subtree:!0}))}syncMobileMenuQuery(){if("matchMedia"in window){if(!this.mobileMenuQuery){this.mobileMenuQuery=window.matchMedia(C),this.mobileMenu=this.mobileMenuQuery.matches,this.mobileMenuQuery.addEventListener("change",this.handleMobileMenuQueryChange);return}this.mobileMenu=this.mobileMenuQuery.matches}}getThemeRoot(){return this.traverseToClosest("esp-root")}syncExternalDrawerTarget(){const e=this.getExternalDrawerTarget(),t=e===this.menu?null:e;t!==this.externalDrawerTarget&&(this.externalDrawerTarget?.removeEventListener("drawer-opened",this.onMenuDrawerOpened),this.externalDrawerTarget?.removeEventListener("drawer-closed",this.onMenuDrawerClosed),this.externalDrawerTarget instanceof u&&(this.externalDrawerTarget.hasExternalDrawerControl=!1),this.externalDrawerTarget=t,this.externalDrawerTarget?.addEventListener("drawer-opened",this.onMenuDrawerOpened),this.externalDrawerTarget?.addEventListener("drawer-closed",this.onMenuDrawerClosed)),this.syncExternalDrawerControl()}burgerVisibleForTarget(){if(this.menuDisplay==="inline")return!1;const e=this.showBurger||this.menuDisplay==="drawer"||this.layout==="minimal";return this.menuTooLarge||this.mobileMenu||e}syncExternalDrawerControl(){this.externalDrawerTarget instanceof u&&(this.externalDrawerTarget.hasExternalDrawerControl=this.burgerVisibleForTarget())}openNav(){const e=this.getBurgerFocusTarget(),t=this.getExternalDrawerTarget();if(this.burger&&(this.burger.menuOpen=!0),t){this.menuOpen=!0,t.openDrawer(e),document.body.classList.add("menu-open");return}if(this.menu){this.menu.sliding=!0,this.menuOpen=!0,this.menu.openDrawer(e),document.body.classList.add("menu-open");return}this.burger&&(this.burger.menuOpen=!1)}closeNav(){this.menuOpen=!1,this.burger&&(this.burger.menuOpen=!1);const e=this.getExternalDrawerTarget();if(e){e.closeDrawer(),document.body.classList.remove("menu-open");return}this.menu&&(this.menu.closeDrawer(),document.body.classList.remove("menu-open"))}get burger(){return this.menuButton.value??void 0}firstUpdated(e){if(super.firstUpdated(e),!this.menuContainer.value)return;const s=(this.menuContainer.value.firstElementChild?.assignedElements()??[])[0];s instanceof u?(this.menu=s,this.addMenuDrawerListeners(),this.observeMenuChildren()):s&&console.warn("<esp-header>: the menu slot expects an <esp-menu> element, received",s),this.observeMenuContainer(),this.syncExternalDrawerTarget(),this.syncMobileMenuQuery(),this.syncScrollListeners(),this.syncMenuLayout()}updated(e){super.updated(e),(e.has("showBurger")||e.has("drawerTarget")||e.has("menuDisplay")||e.has("layout"))&&(this.syncExternalDrawerTarget(),this.syncMenuLayout()),(e.has("mobileMenu")||e.has("menuTooLarge"))&&this.syncExternalDrawerControl(),e.has("scrollBehavior")?this.syncScrollListeners():(e.has("scrollThreshold")||e.has("dockOffset"))&&this.updateScrollState()}connectedCallback(){super.connectedCallback(),this.syncScrollListeners(),this.syncMobileMenuQuery(),this.addMenuDrawerListeners(),this.syncExternalDrawerTarget(),this.observeMenuContainer(),this.observeMenuChildren()}disconnectedCallback(){this.removeMenuDrawerListeners(),this.externalDrawerTarget?.removeEventListener("drawer-opened",this.onMenuDrawerOpened),this.externalDrawerTarget?.removeEventListener("drawer-closed",this.onMenuDrawerClosed),this.externalDrawerTarget instanceof u&&(this.externalDrawerTarget.hasExternalDrawerControl=!1),this.externalDrawerTarget=null,this.mobileMenuQuery?.removeEventListener("change",this.handleMobileMenuQueryChange),this.mobileMenuQuery=null,this.removeScrollListeners(),this.cancelScrollUpdate(),this.disconnectResizeObserver(),this.menuChildObserver?.disconnect(),this.menuChildObserver=null,super.disconnectedCallback()}render(){const{menuOpen:e,menuTooLarge:t,mobileMenu:o}=this,s=this.menuDisplay!=="inline",i=s&&(this.showBurger||this.menuDisplay==="drawer"||this.layout==="minimal"),l=this.getScrollTokens(),d=l.has("progress"),p={"--_esp-header-brand-color":this.brandColor||null,"--_esp-header-scroll-progress":String(this.scrollProgress)},m={"menu-open":e,"too-wide":s&&this.hasDrawerableContent()&&(t||o||i),[`layout-${this.layout}`]:!0,"is-scrolled":this.scrolled,"hide-on-scroll":this.hiddenByScroll,"scroll-compact":l.has("compact"),"scroll-elevate":l.has("elevate"),"scroll-transparent":l.has("transparent")||l.has("transparent-to-solid"),"scroll-collapse-topbar":l.has("collapse-topbar")},y=this.scheme==="dark"?"light":"dark";return g`
|
|
2
|
-
<header class=${M
|
|
1
|
+
var n=function(h,e,t,o){var s=arguments.length,i=s<3?e:o===null?o=Object.getOwnPropertyDescriptor(e,t):o,l;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")i=Reflect.decorate(h,e,t,o);else for(var d=h.length-1;d>=0;d--)(l=h[d])&&(i=(s<3?l(i):s>3?l(e,t,i):l(e,t))||i);return s>3&&i&&Object.defineProperty(e,t,i),i};import{css as C,html as x,nothing as D}from"lit";import{customElement as k,property as a,state as c}from"lit/decorators.js";import{classMap as A}from"lit/directives/class-map.js";import{styleMap as B}from"lit/directives/style-map.js";import"../burger/esp-burger.js";import{createRef as E,ref as L}from"lit/directives/ref.js";import{EspalierMenu as m}from"./esp-menu.js";import{renderConfiguredBrand as O,resolveConfiguredBrand as _}from"../shared/configured-brand.js";import{EspalierElementBase as T}from"../shared/esp-element-base.js";import{ESP_EVENTS as R}from"../shared/events.js";const z="(max-width: 36rem)";function u(h,e){const t=new Set(h);return{allowedValues:t,converter:{fromAttribute(s){return s??e},toAttribute(s){return t.has(s)?s:e}},defaultValue:e}}const H=["standard","centered-brand","extended","extended-centered","minimal"],V=["start","center","end"],U=["truncate","wrap","nowrap"],N=["auto","inline","drawer"],I=["hidden","visible"],g=u(H,"standard"),b=u(V,"start"),f=u(U,"truncate"),v=u(N,"auto"),w=u(I,"hidden");let r=class extends T{constructor(){super(...arguments),this.menuOpen=!1,this.menuTooLarge=!1,this.mobileMenu=!1,this.scrolled=!1,this.hiddenByScroll=!1,this.scrollProgress=0,this.showBurger=!1,this.layout=g.defaultValue,this.brandText="",this.brandLogo="",this.lightBrandLogo="",this.darkBrandLogo="",this.brandHref="",this.brandAlt="",this.brandColor="",this.lightBrandColor="",this.darkBrandColor="",this.brandAlign=b.defaultValue,this.brandWrap=f.defaultValue,this.menuDisplay=v.defaultValue,this.themeToggle=w.defaultValue,this.scrollBehavior="none",this.scrollThreshold=16,this.dockOffset=0,this.drawerTarget="",this.fullBleedContent=!1,this.menuContainer=E(),this.menuButton=E(),this.menu=void 0,this.externalDrawerTarget=null,this.lastScrollY=0,this.lastInlineMenuWidth=0,this.mobileMenuQuery=null,this.menuDrawerListenersAttached=!1,this.observingMenuContainer=!1,this.scrollListenersAttached=!1,this.scrollRafId=null,this.resizeObserver=new ResizeObserver(()=>this.syncMenuLayout()),this.menuChildObserver=null,this.updateScrollState=()=>{this.scrollRafId===null&&(this.scrollRafId=requestAnimationFrame(()=>{this.scrollRafId=null,this.runScrollStateUpdate()}))},this.handleMobileMenuQueryChange=e=>{this.mobileMenu=e.matches,this.syncMenuLayout(),this.syncExternalDrawerControl()},this.toggleTheme=()=>{const e=this.getThemeRoot();if(!e)return;e.scheme=e.scheme==="dark"?"light":"dark";const t=e.scheme==="dark"?"dark":"light";this.dispatchEvent(new CustomEvent(R.ESP_THEME_TOGGLE,{detail:{scheme:t},bubbles:!0,composed:!0}))},this.handleMenuToggleClicked=()=>{if(this.menuOpen){this.closeNav();return}this.openNav()},this.onMenuDrawerOpened=()=>{this.menuOpen=!0,document.body.classList.add("menu-open"),this.burger&&(this.burger.menuOpen=!0)},this.onMenuDrawerClosed=()=>{this.menuOpen=!1,document.body.classList.remove("menu-open"),this.burger&&(this.burger.menuOpen=!1)}}syncMenuLayout(){if(!this.menuContainer.value||!this.menu||!this.menu.getWidth)return;const e=this.menuContainer.value.clientWidth,t=this.menu.getWidth();t>0&&(this.lastInlineMenuWidth=t);const o=this.lastInlineMenuWidth||t,s=this.getExternalDrawerTarget(),i=this.menuDisplay!=="inline",l=this.showBurger||this.menuDisplay==="drawer"||this.layout==="minimal";this.menuTooLarge=i&&this.menuDisplay==="auto"&&o>e,this.menu.sliding=i&&!s&&this.hasDrawerableContent()&&(this.menuTooLarge||l||this.mobileMenu)}menuHasContent(){return!!(this.menu??this.querySelector(':scope > [slot="menu"]'))?.querySelector("esp-menu-item, esp-menu-group")}hasDrawerableContent(){return this.showBurger||!!this.getExternalDrawerTarget()||this.menuHasContent()}getExternalDrawerTarget(){if(!this.drawerTarget)return null;const e=document.getElementById(this.drawerTarget);return e===this.menu?null:e&&"openDrawer"in e&&typeof e.openDrawer=="function"&&"closeDrawer"in e&&typeof e.closeDrawer=="function"?e:null}getBurgerFocusTarget(){const t=this.shadowRoot?.querySelector("esp-header-button.menu-toggle")?.shadowRoot?.querySelector("button"),o=this.menuButton.value?.shadowRoot?.querySelector("button");return t??o??null}getScrollTokens(){const e=this.scrollBehavior.split(/\s+/).map(t=>t.trim().toLowerCase()).filter(Boolean);return new Set(e.filter(t=>t!=="none"))}getEffectiveScrollThreshold(){return Number.isFinite(this.scrollThreshold)?Math.max(0,this.scrollThreshold):0}clearScrollState(){this.scrolled=!1,this.hiddenByScroll=!1,this.scrollProgress=0}runScrollStateUpdate(){const e=this.getScrollTokens();if(!e.size){this.clearScrollState();return}const t=Math.max(0,window.scrollY||document.documentElement.scrollTop||0),o=this.getEffectiveScrollThreshold(),s=Number.isFinite(this.dockOffset)?Math.max(0,this.dockOffset):o,i=t>(e.has("dock")?s:o),l=t>this.lastScrollY,d=e.has("reveal")&&l&&t>o&&!this.menuOpen;if(this.scrolled=i,this.hiddenByScroll=d,e.has("progress")){const p=document.documentElement,y=Math.max(1,p.scrollHeight-window.innerHeight);this.scrollProgress=Math.min(1,t/y)}else this.scrollProgress=0;this.lastScrollY=t}cancelScrollUpdate(){this.scrollRafId!==null&&(cancelAnimationFrame(this.scrollRafId),this.scrollRafId=null)}syncScrollListeners(){const e=this.getScrollTokens().size>0;if(e&&!this.scrollListenersAttached?(window.addEventListener("scroll",this.updateScrollState,{passive:!0}),window.addEventListener("resize",this.updateScrollState,{passive:!0}),this.scrollListenersAttached=!0):!e&&this.scrollListenersAttached&&this.removeScrollListeners(),e){this.updateScrollState();return}this.cancelScrollUpdate(),this.clearScrollState()}removeScrollListeners(){this.scrollListenersAttached&&(window.removeEventListener("scroll",this.updateScrollState),window.removeEventListener("resize",this.updateScrollState),this.scrollListenersAttached=!1)}addMenuDrawerListeners(){!this.menu||this.menuDrawerListenersAttached||(this.menu.addEventListener("drawer-opened",this.onMenuDrawerOpened),this.menu.addEventListener("drawer-closed",this.onMenuDrawerClosed),this.menuDrawerListenersAttached=!0)}removeMenuDrawerListeners(){!this.menu||!this.menuDrawerListenersAttached||(this.menu.removeEventListener("drawer-opened",this.onMenuDrawerOpened),this.menu.removeEventListener("drawer-closed",this.onMenuDrawerClosed),this.menuDrawerListenersAttached=!1)}observeMenuContainer(){!this.menuContainer.value||this.observingMenuContainer||(this.resizeObserver.observe(this.menuContainer.value),this.observingMenuContainer=!0)}disconnectResizeObserver(){this.observingMenuContainer&&(this.resizeObserver.disconnect(),this.observingMenuContainer=!1)}observeMenuChildren(){!this.menu||this.menuChildObserver||(this.menuChildObserver=new MutationObserver(()=>{this.syncMenuLayout(),this.requestUpdate()}),this.menuChildObserver.observe(this.menu,{childList:!0,subtree:!0}))}syncMobileMenuQuery(){if("matchMedia"in window){if(!this.mobileMenuQuery){this.mobileMenuQuery=window.matchMedia(z),this.mobileMenu=this.mobileMenuQuery.matches,this.mobileMenuQuery.addEventListener("change",this.handleMobileMenuQueryChange);return}this.mobileMenu=this.mobileMenuQuery.matches}}getThemeRoot(){return this.traverseToClosest("esp-root")}syncExternalDrawerTarget(){const e=this.getExternalDrawerTarget(),t=e===this.menu?null:e;t!==this.externalDrawerTarget&&(this.externalDrawerTarget?.removeEventListener("drawer-opened",this.onMenuDrawerOpened),this.externalDrawerTarget?.removeEventListener("drawer-closed",this.onMenuDrawerClosed),this.externalDrawerTarget instanceof m&&(this.externalDrawerTarget.hasExternalDrawerControl=!1),this.externalDrawerTarget=t,this.externalDrawerTarget?.addEventListener("drawer-opened",this.onMenuDrawerOpened),this.externalDrawerTarget?.addEventListener("drawer-closed",this.onMenuDrawerClosed)),this.syncExternalDrawerControl()}burgerVisibleForTarget(){if(this.menuDisplay==="inline")return!1;const e=this.showBurger||this.menuDisplay==="drawer"||this.layout==="minimal";return this.menuTooLarge||this.mobileMenu||e}syncExternalDrawerControl(){this.externalDrawerTarget instanceof m&&(this.externalDrawerTarget.hasExternalDrawerControl=this.burgerVisibleForTarget())}openNav(){const e=this.getBurgerFocusTarget(),t=this.getExternalDrawerTarget();if(this.burger&&(this.burger.menuOpen=!0),t){this.menuOpen=!0,t.openDrawer(e),document.body.classList.add("menu-open");return}if(this.menu){this.menu.sliding=!0,this.menuOpen=!0,this.menu.openDrawer(e),document.body.classList.add("menu-open");return}this.burger&&(this.burger.menuOpen=!1)}closeNav(){this.menuOpen=!1,this.burger&&(this.burger.menuOpen=!1);const e=this.getExternalDrawerTarget();if(e){e.closeDrawer(),document.body.classList.remove("menu-open");return}this.menu&&(this.menu.closeDrawer(),document.body.classList.remove("menu-open"))}get burger(){return this.menuButton.value??void 0}willUpdate(e){super.willUpdate(e),e.has("layout")&&!g.allowedValues.has(this.layout)&&(this.layout=g.defaultValue),e.has("brandAlign")&&!b.allowedValues.has(this.brandAlign)&&(this.brandAlign=b.defaultValue),e.has("brandWrap")&&!f.allowedValues.has(this.brandWrap)&&(this.brandWrap=f.defaultValue),e.has("menuDisplay")&&!v.allowedValues.has(this.menuDisplay)&&(this.menuDisplay=v.defaultValue),e.has("themeToggle")&&!w.allowedValues.has(this.themeToggle)&&(this.themeToggle=w.defaultValue)}firstUpdated(e){if(super.firstUpdated(e),!this.menuContainer.value)return;const s=(this.menuContainer.value.firstElementChild?.assignedElements()??[])[0];s instanceof m?(this.menu=s,this.addMenuDrawerListeners(),this.observeMenuChildren()):s&&console.warn("<esp-header>: the menu slot expects an <esp-menu> element, received",s),this.observeMenuContainer(),this.syncExternalDrawerTarget(),this.syncMobileMenuQuery(),this.syncScrollListeners(),this.syncMenuLayout()}updated(e){super.updated(e),(e.has("showBurger")||e.has("drawerTarget")||e.has("menuDisplay")||e.has("layout"))&&(this.syncExternalDrawerTarget(),this.syncMenuLayout()),(e.has("mobileMenu")||e.has("menuTooLarge"))&&this.syncExternalDrawerControl(),e.has("scrollBehavior")?this.syncScrollListeners():(e.has("scrollThreshold")||e.has("dockOffset"))&&this.updateScrollState()}connectedCallback(){super.connectedCallback(),this.syncScrollListeners(),this.syncMobileMenuQuery(),this.addMenuDrawerListeners(),this.syncExternalDrawerTarget(),this.observeMenuContainer(),this.observeMenuChildren()}disconnectedCallback(){this.removeMenuDrawerListeners(),this.externalDrawerTarget?.removeEventListener("drawer-opened",this.onMenuDrawerOpened),this.externalDrawerTarget?.removeEventListener("drawer-closed",this.onMenuDrawerClosed),this.externalDrawerTarget instanceof m&&(this.externalDrawerTarget.hasExternalDrawerControl=!1),this.externalDrawerTarget=null,this.mobileMenuQuery?.removeEventListener("change",this.handleMobileMenuQueryChange),this.mobileMenuQuery=null,this.removeScrollListeners(),this.cancelScrollUpdate(),this.disconnectResizeObserver(),this.menuChildObserver?.disconnect(),this.menuChildObserver=null,super.disconnectedCallback()}render(){const{menuOpen:e,menuTooLarge:t,mobileMenu:o}=this,s=this.menuDisplay!=="inline",i=s&&(this.showBurger||this.menuDisplay==="drawer"||this.layout==="minimal"),l=this.getScrollTokens(),d=l.has("progress"),p=_({scheme:this.scheme,brandLogo:this.brandLogo,brandColor:this.brandColor,lightBrandLogo:this.lightBrandLogo,darkBrandLogo:this.darkBrandLogo,lightBrandColor:this.lightBrandColor,darkBrandColor:this.darkBrandColor}),y={"--_esp-header-brand-color":p.brandColor||null,"--_esp-header-scroll-progress":String(this.scrollProgress)},M={"menu-open":e,"too-wide":s&&this.hasDrawerableContent()&&(t||o||i),[`layout-${this.layout}`]:!0,"is-scrolled":this.scrolled,"hide-on-scroll":this.hiddenByScroll,"scroll-compact":l.has("compact"),"scroll-elevate":l.has("elevate"),"scroll-transparent":l.has("transparent")||l.has("transparent-to-solid"),"scroll-collapse-topbar":l.has("collapse-topbar")},S=this.scheme==="dark"?"light":"dark";return x`
|
|
2
|
+
<header class=${A(M)} style=${B(y)}>
|
|
3
3
|
<section class="topbar">
|
|
4
4
|
<slot name="topbar"></slot>
|
|
5
5
|
</section>
|
|
6
6
|
|
|
7
7
|
<section class="brand">
|
|
8
8
|
<slot name="brand"
|
|
9
|
-
>${
|
|
9
|
+
>${O({brandLogo:p.brandLogo,brandText:this.brandText,brandHref:this.brandHref,brandAlt:this.brandAlt})}</slot
|
|
10
10
|
>
|
|
11
11
|
</section>
|
|
12
12
|
|
|
13
|
-
<section ${
|
|
13
|
+
<section ${L(this.menuContainer)} class="menu-container">
|
|
14
14
|
<slot name="menu"></slot>
|
|
15
15
|
</section>
|
|
16
16
|
|
|
17
17
|
<section class="buttons">
|
|
18
18
|
<slot name="buttons"></slot>
|
|
19
|
-
${this.themeToggle==="visible"?
|
|
19
|
+
${this.themeToggle==="visible"?x`<esp-header-button
|
|
20
20
|
class="theme-toggle"
|
|
21
21
|
icon=${this.scheme==="dark"?"sun":"moon"}
|
|
22
|
-
aria-label=${`Switch to ${
|
|
22
|
+
aria-label=${`Switch to ${S} mode`}
|
|
23
23
|
@clicked=${this.toggleTheme}
|
|
24
|
-
></esp-header-button>`:
|
|
24
|
+
></esp-header-button>`:D}
|
|
25
25
|
<esp-header-button
|
|
26
26
|
class="menu-toggle"
|
|
27
27
|
role="button"
|
|
28
28
|
aria-label=${e?"Close navigation":"Open navigation"}
|
|
29
|
-
aria-controls=${this.drawerTarget||
|
|
29
|
+
aria-controls=${this.drawerTarget||D}
|
|
30
30
|
aria-expanded=${e?"true":"false"}
|
|
31
31
|
@clicked=${this.handleMenuToggleClicked}
|
|
32
32
|
>
|
|
33
|
-
<esp-burger ${
|
|
33
|
+
<esp-burger ${L(this.menuButton)} presentation-only></esp-burger>
|
|
34
34
|
</esp-header-button>
|
|
35
35
|
</section>
|
|
36
|
-
${d?
|
|
36
|
+
${d?x`<div class="scroll-progress" aria-hidden="true"></div>`:D}
|
|
37
37
|
</header>
|
|
38
|
-
`}};r.styles=[...
|
|
38
|
+
`}};r.styles=[...T.styles,C`
|
|
39
39
|
:host {
|
|
40
40
|
--_esp-header-current-height: var(--esp-header-height, calc(4.5 * var(--esp-size-small)));
|
|
41
41
|
|
|
@@ -107,7 +107,6 @@ var n=function(c,e,t,o){var s=arguments.length,i=s<3?e:o===null?o=Object.getOwnP
|
|
|
107
107
|
align-items: center;
|
|
108
108
|
place-content: var(--esp-header-brand-placement, center start);
|
|
109
109
|
padding-inline: var(--esp-header-brand-padding-inline, var(--esp-size-small));
|
|
110
|
-
color: var(--_esp-header-brand-resolved-color);
|
|
111
110
|
|
|
112
111
|
slot[name="brand"] {
|
|
113
112
|
display: flex;
|
|
@@ -320,7 +319,7 @@ var n=function(c,e,t,o){var s=arguments.length,i=s<3?e:o===null?o=Object.getOwnP
|
|
|
320
319
|
color: var(--esp-header-transparent-color, var(--esp-color-headings));
|
|
321
320
|
}
|
|
322
321
|
|
|
323
|
-
&.scroll-transparent:not(.is-scrolled)
|
|
322
|
+
&.scroll-transparent:not(.is-scrolled) .configured-brand {
|
|
324
323
|
color: var(--esp-header-transparent-color, var(--_esp-header-brand-resolved-color));
|
|
325
324
|
}
|
|
326
325
|
|
|
@@ -430,4 +429,4 @@ var n=function(c,e,t,o){var s=arguments.length,i=s<3?e:o===null?o=Object.getOwnP
|
|
|
430
429
|
transition: none;
|
|
431
430
|
}
|
|
432
431
|
}
|
|
433
|
-
`],n([
|
|
432
|
+
`],n([c()],r.prototype,"menuOpen",void 0),n([c()],r.prototype,"menuTooLarge",void 0),n([c()],r.prototype,"mobileMenu",void 0),n([c()],r.prototype,"scrolled",void 0),n([c()],r.prototype,"hiddenByScroll",void 0),n([c()],r.prototype,"scrollProgress",void 0),n([a({attribute:"show-burger",type:Boolean})],r.prototype,"showBurger",void 0),n([a({converter:g.converter,reflect:!0})],r.prototype,"layout",void 0),n([a({attribute:"brand-text",type:String})],r.prototype,"brandText",void 0),n([a({attribute:"brand-logo",type:String})],r.prototype,"brandLogo",void 0),n([a({attribute:"light-brand-logo",type:String})],r.prototype,"lightBrandLogo",void 0),n([a({attribute:"dark-brand-logo",type:String})],r.prototype,"darkBrandLogo",void 0),n([a({attribute:"brand-href",type:String})],r.prototype,"brandHref",void 0),n([a({attribute:"brand-alt",type:String})],r.prototype,"brandAlt",void 0),n([a({attribute:"brand-color",type:String})],r.prototype,"brandColor",void 0),n([a({attribute:"light-brand-color",type:String})],r.prototype,"lightBrandColor",void 0),n([a({attribute:"dark-brand-color",type:String})],r.prototype,"darkBrandColor",void 0),n([a({attribute:"brand-align",converter:b.converter,reflect:!0})],r.prototype,"brandAlign",void 0),n([a({attribute:"brand-wrap",converter:f.converter,reflect:!0})],r.prototype,"brandWrap",void 0),n([a({attribute:"menu-display",converter:v.converter,reflect:!0})],r.prototype,"menuDisplay",void 0),n([a({attribute:"theme-toggle",converter:w.converter,reflect:!0})],r.prototype,"themeToggle",void 0),n([a({attribute:"scroll-behavior",type:String})],r.prototype,"scrollBehavior",void 0),n([a({attribute:"scroll-threshold",type:Number})],r.prototype,"scrollThreshold",void 0),n([a({attribute:"dock-offset",type:Number})],r.prototype,"dockOffset",void 0),n([a({attribute:"drawer-target",type:String})],r.prototype,"drawerTarget",void 0),n([a({attribute:"full-bleed-content",type:Boolean,reflect:!0})],r.prototype,"fullBleedContent",void 0),r=n([k("esp-header")],r);export{r as EspalierHeader};
|
package/dist/root/esp-root.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { LitElement, type PropertyValues } from "lit";
|
|
2
|
-
import { type SeedColorRoot } from "../shared/bus-events.js";
|
|
2
|
+
import { type SchemeEvents, type SeedColorRoot } from "../shared/bus-events.js";
|
|
3
3
|
import { type EspalierTheme } from "../shared/theme.js";
|
|
4
4
|
export type GoogleFontLoadingPolicy = "auto" | "none";
|
|
5
|
+
export type { SchemeEvents } from "../shared/bus-events.js";
|
|
5
6
|
/**
|
|
6
7
|
* Root container for the Espalier design system.
|
|
7
8
|
*
|
|
@@ -26,6 +27,31 @@ export type GoogleFontLoadingPolicy = "auto" | "none";
|
|
|
26
27
|
* </esp-root>
|
|
27
28
|
* ```
|
|
28
29
|
*
|
|
30
|
+
* ### Nested theme previews and scoped events
|
|
31
|
+
*
|
|
32
|
+
* A nested root creates a scoped theme for its descendants. It does not create
|
|
33
|
+
* a separate event bus: every root publishes theme changes through the same
|
|
34
|
+
* Espalier singleton. Subscribe through the root whose changes you own so a
|
|
35
|
+
* nested preview cannot be mistaken for an application-level theme change.
|
|
36
|
+
*
|
|
37
|
+
* ```ts
|
|
38
|
+
* const applicationRoot = document.querySelector<EspalierRoot>("#application-theme")!;
|
|
39
|
+
*
|
|
40
|
+
* const unsubscribe = applicationRoot.subscribeScoped("scheme-changed", ({ scheme }) => {
|
|
41
|
+
* localStorage.setItem("scheme", scheme);
|
|
42
|
+
* });
|
|
43
|
+
*
|
|
44
|
+
* // Initial state is read directly; scoped events report later changes only.
|
|
45
|
+
* renderScheme(applicationRoot.scheme);
|
|
46
|
+
*
|
|
47
|
+
* // Call when the owning integration is disposed.
|
|
48
|
+
* unsubscribe();
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* This contract covers `scheme-changed`, `seed-color-changed`,
|
|
52
|
+
* `theme-changed`, and `icon-sprite-url-changed`. Components derived from
|
|
53
|
+
* `EspalierElementBase` use the same closest-root filter internally.
|
|
54
|
+
*
|
|
29
55
|
* @customElement esp-root
|
|
30
56
|
* @slot - Place Espalier components and content here.
|
|
31
57
|
*
|
|
@@ -40,6 +66,17 @@ export type GoogleFontLoadingPolicy = "auto" | "none";
|
|
|
40
66
|
export declare class EspalierRoot extends LitElement implements SeedColorRoot {
|
|
41
67
|
/** Unique ID for correlating bus events across multiple roots. */
|
|
42
68
|
correlationId: `${string}-${string}-${string}-${string}-${string}`;
|
|
69
|
+
/**
|
|
70
|
+
* Subscribe to a theme event published by this root only.
|
|
71
|
+
*
|
|
72
|
+
* All roots share Espalier's global bus. This method filters foreign and
|
|
73
|
+
* nested-root payloads by `correlationId` and returns a cleanup function.
|
|
74
|
+
* It reports changes after mount; read initial state from this root.
|
|
75
|
+
*
|
|
76
|
+
* @param event Root-scoped theme event to observe.
|
|
77
|
+
* @param handler Callback invoked with that event's typed payload.
|
|
78
|
+
*/
|
|
79
|
+
subscribeScoped<K extends keyof SchemeEvents>(event: K, handler: (payload: SchemeEvents[K]) => void): () => void;
|
|
43
80
|
/**
|
|
44
81
|
* Base64-encoded JSON partial theme for the **light** scheme.
|
|
45
82
|
*
|
|
@@ -178,6 +215,7 @@ export declare class EspalierRoot extends LitElement implements SeedColorRoot {
|
|
|
178
215
|
get seedColor(): string;
|
|
179
216
|
/** Returns the resolved theme for the active scheme. */
|
|
180
217
|
get activeTheme(): EspalierTheme;
|
|
218
|
+
connectedCallback(): void;
|
|
181
219
|
protected willUpdate(changed: PropertyValues): void;
|
|
182
220
|
protected firstUpdated(): void;
|
|
183
221
|
protected updated(changedProperties: PropertyValues): void;
|