@taprootio/espalier 4.20.2 → 5.0.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.
Files changed (56) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/custom-elements.json +15517 -13314
  3. package/dist/checkbox/esp-checkbox-group.d.ts +6 -0
  4. package/dist/checkbox/esp-checkbox-group.js +10 -5
  5. package/dist/checkbox/esp-checkbox.d.ts +24 -0
  6. package/dist/checkbox/esp-checkbox.js +3 -3
  7. package/dist/cli/theme-check.js +2 -2
  8. package/dist/date-picker/esp-date-picker.js +6 -6
  9. package/dist/flyout/esp-flyout.d.ts +1 -1
  10. package/dist/flyout/esp-flyout.js +3 -9
  11. package/dist/font-picker/esp-font-picker.js +3 -3
  12. package/dist/footer/esp-footer.d.ts +1 -1
  13. package/dist/form-item/esp-form-item.d.ts +35 -0
  14. package/dist/form-item/esp-form-item.js +16 -11
  15. package/dist/image/esp-image.d.ts +2 -2
  16. package/dist/image/esp-image.js +4 -4
  17. package/dist/image/image-ratio.d.ts +5 -0
  18. package/dist/image/image-ratio.js +1 -0
  19. package/dist/image-picker/esp-image-picker.js +2 -2
  20. package/dist/index.d.ts +2 -1
  21. package/dist/index.js +1 -1
  22. package/dist/input/esp-input.d.ts +29 -3
  23. package/dist/input/esp-input.js +14 -14
  24. package/dist/menu/esp-menu.d.ts +0 -5
  25. package/dist/menu/esp-menu.js +4 -4
  26. package/dist/page/esp-page.d.ts +2 -17
  27. package/dist/page/esp-page.js +2 -2
  28. package/dist/page/esp-page.styles.js +8 -32
  29. package/dist/pickers/esp-pick-one.js +7 -7
  30. package/dist/pickers/esp-pick-some.js +8 -8
  31. package/dist/pickers/esp-picker-base.js +2 -2
  32. package/dist/pickers/esp-picker-menu.js +2 -2
  33. package/dist/radio-button/esp-radio-button-group.d.ts +6 -0
  34. package/dist/radio-button/esp-radio-button-group.js +5 -4
  35. package/dist/radio-button/esp-radio-button.d.ts +22 -0
  36. package/dist/radio-button/esp-radio-button.js +2 -2
  37. package/dist/root/esp-root.js +2 -2
  38. package/dist/shared/enum-converter.js +1 -0
  39. package/dist/shared/events.d.ts +11 -0
  40. package/dist/shared/events.js +1 -1
  41. package/dist/shared/font-helpers.d.ts +0 -2
  42. package/dist/shared/font-helpers.js +1 -1
  43. package/dist/shared/form-field-description-controller.js +1 -1
  44. package/dist/shared/form-item-relay.js +1 -0
  45. package/dist/shared/justified-layout.d.ts +5 -31
  46. package/dist/shared/justified-layout.js +1 -1
  47. package/dist/slider/esp-slider.js +1 -1
  48. package/dist/switch/esp-switch.js +3 -3
  49. package/dist/textarea/esp-textarea.js +2 -2
  50. package/dist/video/esp-video.d.ts +266 -0
  51. package/dist/video/esp-video.js +253 -0
  52. package/espalier.css-data.json +40 -16
  53. package/espalier.token-manifest.json +60 -24
  54. package/licenses/ASSET_PROVENANCE.md +1 -0
  55. package/licenses/asset-provenance.json +22 -0
  56. package/package.json +5 -1
@@ -1,4 +1,4 @@
1
- var s=function(a,e,i,l){var c=arguments.length,o=c<3?e:l===null?l=Object.getOwnPropertyDescriptor(e,i):l,n;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")o=Reflect.decorate(a,e,i,l);else for(var h=a.length-1;h>=0;h--)(n=a[h])&&(o=(c<3?n(o):c>3?n(e,i,o):n(e,i))||o);return c>3&&o&&Object.defineProperty(e,i,o),o};import{css as m,html as d}from"lit";import{customElement as v,property as r}from"lit/decorators.js";import{classMap as p}from"lit/directives/class-map.js";import{EspalierElementBase as u}from"../shared/esp-element-base.js";import{FormFieldController as b}from"../shared/form-field-controller.js";import{FormFieldDescriptionController as g}from"../shared/form-field-description-controller.js";import{disabledControl as w,focusRing as f,srOnly as y}from"../shared/style-fragments.js";let t=class extends u{constructor(){super(...arguments),this.internals=this.attachInternals(),this.formCtrl=new b({host:this,internals:this.internals,getFormValue:()=>this.checked?this.value||"on":null,getValidity:()=>this.required&&!this.checked?{flags:{valueMissing:!0},message:this.requiredMessage||"Please toggle this switch to continue."}:null,onReset:()=>{this.checked=!1},onRestore:e=>{this.checked=e==="on"||e===this.value},onDisabled:e=>{this.disabled=e}}),this.formItemDescription=new g({host:this,getTarget:()=>this.shadowRoot?.querySelector(".switch-control")}),this.mode="switch",this.offLabel="",this.onLabel="",this.checked=!1,this.value="",this.name="",this.required=!1,this.requiredMessage="",this.disabled=!1}focus(e){this.focusShadowElementAfterUpdate(".switch-control",e)}setFormItemDescription(e){this.formItemDescription.setDescription(e)}validate(){this.formCtrl.validate()}checkValidity(){return this.formCtrl.checkValidity()}toggle(){this.disabled||(this.checked=!this.checked,this.formCtrl.syncValue(),this.emitValueChanged({checked:this.checked,value:this.value}))}handleKeyDown(e){(e.key===" "||e.key==="Enter")&&(e.preventDefault(),this.toggle())}handleLabelSlotChange(){this.requestUpdate()}getCurrentTextModeLabel(){return(this.checked?this.onLabel:this.offLabel).trim()}getTextModeAriaLabel(){const e=this.textContent?.replace(/\s+/g," ").trim()??"",i=this.getCurrentTextModeLabel();return e&&i?`${e}: ${i}`:e||i}formResetCallback(){this.formCtrl.handleFormReset()}formStateRestoreCallback(e){this.formCtrl.handleFormStateRestore(e)}formDisabledCallback(e){this.formCtrl.handleFormDisabled(e)}render(){return this.mode==="text"?d`
1
+ var r=function(a,e,s,l){var c=arguments.length,o=c<3?e:l===null?l=Object.getOwnPropertyDescriptor(e,s):l,n;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")o=Reflect.decorate(a,e,s,l);else for(var h=a.length-1;h>=0;h--)(n=a[h])&&(o=(c<3?n(o):c>3?n(e,s,o):n(e,s))||o);return c>3&&o&&Object.defineProperty(e,s,o),o};import{css as f,html as d}from"lit";import{customElement as v,property as i}from"lit/decorators.js";import{classMap as p}from"lit/directives/class-map.js";import{EspalierElementBase as u}from"../shared/esp-element-base.js";import{FormFieldController as b}from"../shared/form-field-controller.js";import{FormFieldDescriptionController as g}from"../shared/form-field-description-controller.js";import{disabledControl as w,focusRing as m,srOnly as y}from"../shared/style-fragments.js";let t=class extends u{constructor(){super(...arguments),this.internals=this.attachInternals(),this.formCtrl=new b({host:this,internals:this.internals,getFormValue:()=>this.checked?this.value||"on":null,getValidity:()=>this.required&&!this.checked?{flags:{valueMissing:!0},message:this.requiredMessage||"Please toggle this switch to continue."}:null,onReset:()=>{this.checked=!1},onRestore:e=>{this.checked=e==="on"||e===this.value},onDisabled:e=>{this.disabled=e}}),this.formItemDescription=new g({host:this,getTarget:()=>this.shadowRoot?.querySelector(".switch-control")}),this.mode="switch",this.offLabel="",this.onLabel="",this.checked=!1,this.value="",this.name="",this.required=!1,this.requiredMessage="",this.disabled=!1}focus(e){this.focusShadowElementAfterUpdate(".switch-control",e)}setFormItemDescription(e){this.formItemDescription.setDescription(e)}setFormItemInvalid(e){this.formItemDescription.setInvalid(e)}validate(){this.formCtrl.validate()}checkValidity(){return this.formCtrl.checkValidity()}toggle(){this.disabled||(this.checked=!this.checked,this.formCtrl.syncValue(),this.emitValueChanged({checked:this.checked,value:this.value}))}handleKeyDown(e){(e.key===" "||e.key==="Enter")&&(e.preventDefault(),this.toggle())}handleLabelSlotChange(){this.requestUpdate()}getCurrentTextModeLabel(){return(this.checked?this.onLabel:this.offLabel).trim()}getTextModeAriaLabel(){const e=this.textContent?.replace(/\s+/g," ").trim()??"",s=this.getCurrentTextModeLabel();return e&&s?`${e}: ${s}`:e||s}formResetCallback(){this.formCtrl.handleFormReset()}formStateRestoreCallback(e){this.formCtrl.handleFormStateRestore(e)}formDisabledCallback(e){this.formCtrl.handleFormDisabled(e)}render(){return this.mode==="text"?d`
2
2
  <div
3
3
  class="switch-control text-control"
4
4
  role="switch"
@@ -34,7 +34,7 @@ var s=function(a,e,i,l){var c=arguments.length,o=c<3?e:l===null?l=Object.getOwnP
34
34
  </div>
35
35
  <span class="label"><slot></slot></span>
36
36
  </div>
37
- `}};t.formAssociated=!0,t.styles=[...u.styles,y,f(".switch-control:focus-visible","--esp-switch-focus-shadow"),f(".text-control:focus-visible","--esp-switch-focus-shadow"),w(".switch-control"),m`
37
+ `}};t.clearsFormItemErrorOnChange=!0,t.formAssociated=!0,t.styles=[...u.styles,y,m(".switch-control:focus-visible","--esp-switch-focus-shadow"),m(".text-control:focus-visible","--esp-switch-focus-shadow"),w(".switch-control"),f`
38
38
  :host {
39
39
  display: block;
40
40
  }
@@ -153,4 +153,4 @@ var s=function(a,e,i,l){var c=arguments.length,o=c<3?e:l===null?l=Object.getOwnP
153
153
  .text-option.active {
154
154
  color: var(--esp-switch-text-active-color, var(--esp-color-action-text));
155
155
  }
156
- `],s([r({type:String,reflect:!0})],t.prototype,"mode",void 0),s([r({attribute:"off-label"})],t.prototype,"offLabel",void 0),s([r({attribute:"on-label"})],t.prototype,"onLabel",void 0),s([r({type:Boolean,reflect:!0})],t.prototype,"checked",void 0),s([r({type:String})],t.prototype,"value",void 0),s([r({type:String,reflect:!0})],t.prototype,"name",void 0),s([r({type:Boolean,reflect:!0})],t.prototype,"required",void 0),s([r({attribute:"required-message"})],t.prototype,"requiredMessage",void 0),s([r({type:Boolean,reflect:!0})],t.prototype,"disabled",void 0),t=s([v("esp-switch")],t);export{t as EspalierSwitch};
156
+ `],r([i({type:String,reflect:!0})],t.prototype,"mode",void 0),r([i({attribute:"off-label"})],t.prototype,"offLabel",void 0),r([i({attribute:"on-label"})],t.prototype,"onLabel",void 0),r([i({type:Boolean,reflect:!0})],t.prototype,"checked",void 0),r([i({type:String})],t.prototype,"value",void 0),r([i({type:String,reflect:!0})],t.prototype,"name",void 0),r([i({type:Boolean,reflect:!0})],t.prototype,"required",void 0),r([i({attribute:"required-message"})],t.prototype,"requiredMessage",void 0),r([i({type:Boolean,reflect:!0})],t.prototype,"disabled",void 0),t=r([v("esp-switch")],t);export{t as EspalierSwitch};
@@ -1,4 +1,4 @@
1
- var r=function(n,e,i,l){var h=arguments.length,a=h<3?e:l===null?l=Object.getOwnPropertyDescriptor(e,i):l,d;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")a=Reflect.decorate(n,e,i,l);else for(var u=n.length-1;u>=0;u--)(d=n[u])&&(a=(h<3?d(a):h>3?d(e,i,a):d(e,i))||a);return h>3&&a&&Object.defineProperty(e,i,a),a};import{css as m,html as p}from"lit";import{customElement as f,property as s}from"lit/decorators.js";import{ifDefined as o}from"lit/directives/if-defined.js";import{createRef as v,ref as g}from"lit/directives/ref.js";import{EspalierElementBase as c}from"../shared/esp-element-base.js";import{FormFieldController as y}from"../shared/form-field-controller.js";import{FormFieldDescriptionController as b}from"../shared/form-field-description-controller.js";let t=class extends c{constructor(){super(...arguments),this.internals=this.attachInternals(),this.theTextarea=v(),this.formCtrl=new y({host:this,internals:this.internals,getFormValue:()=>this.value||null,getValidity:()=>this._getValidity(),getValidationAnchor:()=>this.theTextarea.value??this,onReset:()=>{this.value="",this._hasVisibleError=!1},onRestore:e=>{this.value=e},onDisabled:e=>{this.disabled=e}}),this.formItemDescription=new b({host:this,getTarget:()=>this.theTextarea.value}),this._hasVisibleError=!1,this.fieldName="",this.value="",this.name="",this.required=!1,this.requiredMessage="",this.validationMessage="",this.disabled=!1,this._trackVisibleError=e=>{const i=e.detail;this._hasVisibleError=!i.valid}}handleInput(e){const i=e.target;this.value=i.value,this._syncInput()}_syncInput(){this._hasVisibleError?this.formCtrl.syncValue():this.formCtrl.syncValueSilently(),this.emitValueChanged(this.value)}_handleBlur(){this.formCtrl.syncValue()}_getValidity(){const e=this.theTextarea.value;if(e&&!e.validity.valid){const i=e.validity,l={valueMissing:i.valueMissing,tooLong:i.tooLong,tooShort:i.tooShort};return{flags:l,message:this._getFriendlyMessage(l)}}return this.required&&!this.value?{flags:{valueMissing:!0},message:this.requiredMessage||"Please fill out this field."}:null}_getFriendlyMessage(e){return e.valueMissing?this.requiredMessage||"Please fill out this field.":e.tooShort?this.validationMessage||`Must be at least ${this.minlength} characters.`:e.tooLong?this.validationMessage||`Must be no more than ${this.maxlength} characters.`:"Please enter a valid value."}focus(e){this.focusResolvedElementAfterUpdate(()=>this.theTextarea.value,e)}setFormItemDescription(e){this.formItemDescription.setDescription(e)}setFormItemLabel(e){this.formItemDescription.setLabel(e)}validate(){this.formCtrl.validate()}checkValidity(){return this.formCtrl.checkValidity()}formResetCallback(){this.formCtrl.handleFormReset()}formStateRestoreCallback(e){this.formCtrl.handleFormStateRestore(e)}formDisabledCallback(e){this.formCtrl.handleFormDisabled(e)}connectedCallback(){super.connectedCallback(),this.addEventListener("esp-validity-changed",this._trackVisibleError)}disconnectedCallback(){super.disconnectedCallback(),this.removeEventListener("esp-validity-changed",this._trackVisibleError)}render(){return p`
1
+ var r=function(n,e,i,l){var h=arguments.length,a=h<3?e:l===null?l=Object.getOwnPropertyDescriptor(e,i):l,d;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")a=Reflect.decorate(n,e,i,l);else for(var u=n.length-1;u>=0;u--)(d=n[u])&&(a=(h<3?d(a):h>3?d(e,i,a):d(e,i))||a);return h>3&&a&&Object.defineProperty(e,i,a),a};import{css as c,html as p}from"lit";import{customElement as f,property as s}from"lit/decorators.js";import{ifDefined as o}from"lit/directives/if-defined.js";import{createRef as v,ref as g}from"lit/directives/ref.js";import{EspalierElementBase as m}from"../shared/esp-element-base.js";import{FormFieldController as y}from"../shared/form-field-controller.js";import{FormFieldDescriptionController as b}from"../shared/form-field-description-controller.js";let t=class extends m{constructor(){super(...arguments),this.internals=this.attachInternals(),this.theTextarea=v(),this.formCtrl=new y({host:this,internals:this.internals,getFormValue:()=>this.value||null,getValidity:()=>this._getValidity(),getValidationAnchor:()=>this.theTextarea.value??this,onReset:()=>{this.value="",this._hasVisibleError=!1},onRestore:e=>{this.value=e},onDisabled:e=>{this.disabled=e}}),this.formItemDescription=new b({host:this,getTarget:()=>this.theTextarea.value}),this._hasVisibleError=!1,this.fieldName="",this.value="",this.name="",this.required=!1,this.requiredMessage="",this.validationMessage="",this.disabled=!1,this._trackVisibleError=e=>{const i=e.detail;this._hasVisibleError=!i.valid}}handleInput(e){const i=e.target;this.value=i.value,this._syncInput()}_syncInput(){this._hasVisibleError?this.formCtrl.syncValue():this.formCtrl.syncValueSilently(),this.emitValueChanged(this.value)}_handleBlur(){this.formCtrl.syncValue()}_getValidity(){const e=this.theTextarea.value;if(e&&!e.validity.valid){const i=e.validity,l={valueMissing:i.valueMissing,tooLong:i.tooLong,tooShort:i.tooShort};return{flags:l,message:this._getFriendlyMessage(l)}}return this.required&&!this.value?{flags:{valueMissing:!0},message:this.requiredMessage||"Please fill out this field."}:null}_getFriendlyMessage(e){return e.valueMissing?this.requiredMessage||"Please fill out this field.":e.tooShort?this.validationMessage||`Must be at least ${this.minlength} characters.`:e.tooLong?this.validationMessage||`Must be no more than ${this.maxlength} characters.`:"Please enter a valid value."}focus(e){this.focusResolvedElementAfterUpdate(()=>this.theTextarea.value,e)}setFormItemDescription(e){this.formItemDescription.setDescription(e)}setFormItemInvalid(e){this.formItemDescription.setInvalid(e)}setFormItemLabel(e){this.formItemDescription.setLabel(e)}validate(){this.formCtrl.validate()}checkValidity(){return this.formCtrl.checkValidity()}formResetCallback(){this.formCtrl.handleFormReset()}formStateRestoreCallback(e){this.formCtrl.handleFormStateRestore(e)}formDisabledCallback(e){this.formCtrl.handleFormDisabled(e)}connectedCallback(){super.connectedCallback(),this.addEventListener("esp-validity-changed",this._trackVisibleError)}disconnectedCallback(){super.disconnectedCallback(),this.removeEventListener("esp-validity-changed",this._trackVisibleError)}render(){return p`
2
2
  <div class="esp-field">
3
3
  <textarea
4
4
  id=${this.fieldName}
@@ -15,7 +15,7 @@ var r=function(n,e,i,l){var h=arguments.length,a=h<3?e:l===null?l=Object.getOwnP
15
15
  ?disabled=${this.disabled}
16
16
  ></textarea>
17
17
  </div>
18
- `}};t.formAssociated=!0,t.styles=[...c.styles,m`
18
+ `}};t.formAssociated=!0,t.styles=[...m.styles,c`
19
19
  .esp-field {
20
20
  margin: 1px;
21
21
 
@@ -0,0 +1,266 @@
1
+ import { type PropertyValues } from "lit";
2
+ import { EspalierElementBase } from "../shared/esp-element-base.js";
3
+ /** The HLS master-playlist media type, and the default `type`. */
4
+ export declare const HLS_MEDIA_TYPE = "application/vnd.apple.mpegurl";
5
+ /** The `aspect-ratio` used when the attribute is absent or invalid. */
6
+ export declare const DEFAULT_VIDEO_ASPECT_RATIO = "16 / 9";
7
+ export type VideoPreload = "none" | "metadata" | "auto";
8
+ export type VideoControls = "native" | "none";
9
+ export type VideoCrossOrigin = "" | "anonymous" | "use-credentials";
10
+ /**
11
+ * Attaches a stream the browser cannot play natively to the element's
12
+ * `<video>`. Called with the video element and the `src` URL; returns, or
13
+ * resolves to, a teardown function the element calls when the source goes
14
+ * away. A rejection (or a synchronous throw) is a failed attach.
15
+ */
16
+ export type VideoAdapter = (video: HTMLVideoElement, src: string) => Promise<() => void> | (() => void);
17
+ /**
18
+ * How the playing source reached the video: `native` set `src` directly,
19
+ * `adapter` went through the host's adapter, and `fallback` is playing
20
+ * `fallback-src`.
21
+ */
22
+ export type VideoSourceMode = "native" | "adapter" | "fallback";
23
+ /**
24
+ * Why playback failed: `unsupported` when no `src` is set (and no fallback),
25
+ * or nothing could play `type` (no native support, no adapter, no fallback), `adapter` when the adapter rejected or
26
+ * threw, and `media` when the video element reported an error while loading
27
+ * or playing.
28
+ */
29
+ export type VideoErrorReason = "unsupported" | "adapter" | "media";
30
+ /**
31
+ * Detail of `esp-video-ready` on `esp-video`.
32
+ *
33
+ * @docUrl /api/video-ready-detail
34
+ * @menuGroup Event Details
35
+ * @menuLabel VideoReadyDetail
36
+ */
37
+ export interface VideoReadyDetail {
38
+ /** The element's `src`. */
39
+ src: string;
40
+ /** How the source was attached. */
41
+ mode: VideoSourceMode;
42
+ /** The media duration in seconds; `Infinity` for a live stream. */
43
+ duration: number;
44
+ }
45
+ /**
46
+ * Detail of `esp-video-play` on `esp-video`.
47
+ *
48
+ * @docUrl /api/video-play-detail
49
+ * @menuGroup Event Details
50
+ * @menuLabel VideoPlayDetail
51
+ */
52
+ export interface VideoPlayDetail {
53
+ /** The element's `src`. */
54
+ src: string;
55
+ /** How the source was attached. */
56
+ mode: VideoSourceMode;
57
+ /** The playback position, in seconds, at which playback started. */
58
+ currentTime: number;
59
+ /** Whether `autoplay` started this playback rather than the visitor. */
60
+ autoplay: boolean;
61
+ }
62
+ /**
63
+ * Detail of `esp-video-error` on `esp-video`.
64
+ *
65
+ * @docUrl /api/video-error-detail
66
+ * @menuGroup Event Details
67
+ * @menuLabel VideoErrorDetail
68
+ */
69
+ export interface VideoErrorDetail {
70
+ /** The element's `src`. */
71
+ src: string;
72
+ /** The attachment that failed, or `null` when nothing was attached. */
73
+ mode: VideoSourceMode | null;
74
+ /** Why playback failed. */
75
+ reason: VideoErrorReason;
76
+ /** A short developer-facing description; not shown to visitors. */
77
+ message: string;
78
+ /** Whether the element is now trying `fallback-src`. */
79
+ fallback: boolean;
80
+ }
81
+ /**
82
+ * A native video player: a `<video>` element with a lazy poster, a fixed
83
+ * aspect ratio, captions, native controls, and visible loading and error
84
+ * states. It is never an iframe and never loads a third-party player.
85
+ *
86
+ * Nothing but the poster loads until the visitor presses play, so a page of
87
+ * videos costs one image request each. The play affordance over the poster is
88
+ * a real button named from `label` or `poster-alt`.
89
+ *
90
+ * ```html
91
+ * <esp-video
92
+ * src="/assets/esp-video/espalier-sample.mp4"
93
+ * type="video/mp4"
94
+ * poster="/assets/esp-video/espalier-sample-poster.jpg"
95
+ * poster-alt="Espalier's palette drifting across a trellis grid"
96
+ * playsinline
97
+ * ></esp-video>
98
+ * ```
99
+ *
100
+ * ### Streaming
101
+ *
102
+ * `src` is an HLS master playlist by default. Where the browser plays HLS
103
+ * natively (Safari, iOS, current Chrome) the element sets `src` and plays.
104
+ * Where it does not, the element calls the host's `adapter` once, on first
105
+ * play intent, and the host attaches a streaming library such as hls.js.
106
+ * Espalier itself carries no player dependency. Without an adapter,
107
+ * `fallback-src` (an MP4) plays instead. The Video guide has a copy-ready
108
+ * hls.js adapter, with error handling, and the full lifecycle.
109
+ *
110
+ * ```js
111
+ * import { EspalierVideo } from "@taprootio/espalier/video";
112
+ * EspalierVideo.defaultAdapter = async (video, src) => {
113
+ * const { default: Hls } = await import("hls.js");
114
+ * const hls = new Hls();
115
+ * hls.loadSource(src);
116
+ * hls.attachMedia(video);
117
+ * return () => hls.destroy();
118
+ * };
119
+ * ```
120
+ *
121
+ * ### Captions
122
+ *
123
+ * Give the element `<track>` children. A media element only reads its own
124
+ * child tracks, so the element mirrors them into its video when it attaches
125
+ * the source; `kind`, `srclang`, `label`, and `default` carry through. With
126
+ * the default `preload="none"`, no caption file loads before the visitor
127
+ * presses play.
128
+ *
129
+ * ```html
130
+ * <esp-video
131
+ * src="/assets/esp-video/espalier-sample.mp4"
132
+ * type="video/mp4"
133
+ * poster="/assets/esp-video/espalier-sample-poster.jpg"
134
+ * poster-alt="The trellis sample, with English captions"
135
+ * aspect-ratio="21 / 9"
136
+ * >
137
+ * <track kind="captions" src="/assets/esp-video/espalier-sample.en.vtt" srclang="en" label="English" default />
138
+ * </esp-video>
139
+ * ```
140
+ *
141
+ * ### Autoplay
142
+ *
143
+ * `autoplay` starts playback once the element is admitted (immediately, or
144
+ * near the viewport with `defer-offscreen`). It always plays muted: while
145
+ * `autoplay` is set the video stays muted whatever `muted` says, so a page
146
+ * never starts sound unprompted. It does not start for a visitor who prefers
147
+ * reduced motion, or where the browser blocks it; the poster and play button
148
+ * remain.
149
+ *
150
+ * @slot - `<track>` elements for captions, subtitles, descriptions, and chapters.
151
+ * @slot error - Replaces the error message shown over the poster.
152
+ * @csspart frame - The element's full box.
153
+ * @csspart video - The native `<video>` element.
154
+ * @csspart poster - The layer over the video holding the poster, play button, and messages.
155
+ * @csspart poster-image - The poster `<img>`.
156
+ * @csspart play - The play `<button>`, which covers the poster.
157
+ * @csspart play-icon - The visible play disc inside the button.
158
+ * @csspart message - The error message.
159
+ * @cssprop --esp-video-surface - Background behind the video and an absent poster. Defaults to a near-black derived from `--esp-color-text`.
160
+ * @cssprop --esp-video-border-radius - Corner radius. Defaults to `var(--esp-size-border-radius)`.
161
+ * @cssprop --esp-video-object-fit - How the video fills its box. Defaults to `contain`; the poster always covers.
162
+ * @cssprop --esp-video-accent - `accent-color` for the native controls, where the browser honors it. Defaults to `var(--esp-color-primary)`.
163
+ * @cssprop --esp-video-focus-outline - Focus-visible outline on the play button, the video, and the error message. Defaults to `3px solid var(--esp-color-link)`.
164
+ * @cssprop --esp-video-play-size - Diameter of the play disc. Defaults to `var(--esp-size-huge)`.
165
+ * @cssprop --esp-video-play-background - Fill of the play disc. Defaults to `var(--esp-color-background)` at 90% opacity.
166
+ * @cssprop --esp-video-play-color - Play glyph and loading-indicator color. Defaults to `var(--esp-color-text)`.
167
+ * @cssprop --esp-video-message-background - Error message background. Defaults to `var(--esp-color-background)`.
168
+ * @cssprop --esp-video-message-color - Error message text color. Defaults to `var(--esp-color-text)`.
169
+ * @event {CustomEvent<VideoReadyDetail>} esp-video-ready - Bubbles across shadow boundaries once the attached source has loaded its metadata; `detail` is `{ src, mode, duration }`. Fires once per attached source.
170
+ * @event {CustomEvent<VideoPlayDetail>} esp-video-play - Bubbles across shadow boundaries each time playback starts, or resumes after a pause, on the media's `playing`. Recovery from buffering or a seek is not reported, and a source that fails first reports no play; `detail` is `{ src, mode, currentTime, autoplay }`.
171
+ * @event {CustomEvent<VideoErrorDetail>} esp-video-error - Bubbles across shadow boundaries when a source fails; `detail` is `{ src, mode, reason, message, fallback }`. When `fallback` is true the element is already trying `fallback-src`.
172
+ * @docPageTitle Video
173
+ * @docUrl /components/video
174
+ * @menuGroup Media
175
+ * @menuLabel Video
176
+ * @menuIcon photo
177
+ */
178
+ export declare class EspalierVideo extends EspalierElementBase {
179
+ /**
180
+ * The adapter used by every `esp-video` whose own `adapter` is unset.
181
+ * Register it once, before any video can receive a play intent — in the
182
+ * same module that imports Espalier is early enough, even for `autoplay`.
183
+ */
184
+ static defaultAdapter: VideoAdapter | null;
185
+ /** The media URL: an HLS master playlist by default, or a file matching `type`. */
186
+ src: string;
187
+ /**
188
+ * The media type of `src`, checked with `canPlayType` to choose between
189
+ * native playback and the adapter. `video/mp4` plays as a plain file.
190
+ */
191
+ type: string;
192
+ /** Poster image URL, shown until playback starts and behind error messages. */
193
+ poster: string;
194
+ /** Describes the poster; names the play button when `label` is unset. */
195
+ posterAlt: string;
196
+ /**
197
+ * The play button's accessible name. Defaults to `Play video: <poster-alt>`,
198
+ * or `Play video` with no poster text.
199
+ */
200
+ label: string;
201
+ /**
202
+ * The box's aspect ratio, as a positive number or `number / number`. Invalid
203
+ * values fall back to `16 / 9`.
204
+ */
205
+ aspectRatio: string;
206
+ /**
207
+ * How much media to load before play intent. `none` (the default) loads
208
+ * only the poster. `metadata` and `auto` apply where the browser plays
209
+ * `type` natively; an adapter stream, and the choice of the fallback over
210
+ * it, still wait for play intent. A natively preloaded source that fails
211
+ * switches to `fallback-src` at once.
212
+ */
213
+ preload: VideoPreload;
214
+ /** Start muted playback once admitted. Implies `muted`. */
215
+ autoplay: boolean;
216
+ /**
217
+ * Start muted. Always on while `autoplay` is set. Clearing it (or
218
+ * `autoplay`) unmutes only a paused video; a playing one stays muted until
219
+ * the visitor unmutes it.
220
+ */
221
+ muted: boolean;
222
+ /** Restart from the beginning when playback ends. */
223
+ loop: boolean;
224
+ /** Play inline on iOS rather than entering full screen. Needed for inline autoplay there. */
225
+ playsinline: boolean;
226
+ /**
227
+ * `native` (the default) shows the browser's controls once playback starts.
228
+ * `none` shows none; use it only for short decorative loops, since a visitor
229
+ * then has no way to pause.
230
+ */
231
+ controls: VideoControls;
232
+ /**
233
+ * Defer everything — the poster, `preload`, and `autoplay` — until the
234
+ * element is within 200px of the viewport. The poster is not rendered
235
+ * before then, so the browser's own lazy-loading distance cannot fetch it
236
+ * early.
237
+ */
238
+ deferOffscreen: boolean;
239
+ /**
240
+ * An MP4 played when `type` cannot play natively and no adapter is
241
+ * available, and tried once when the primary source fails.
242
+ */
243
+ fallbackSrc: string;
244
+ /**
245
+ * CORS mode for the video's requests, as on `<video crossorigin>`. Needed
246
+ * for caption tracks served from another origin.
247
+ */
248
+ crossOrigin: VideoCrossOrigin;
249
+ /**
250
+ * Attaches a stream the browser cannot play natively; see `VideoAdapter`.
251
+ * Called once per source, on first play intent, only when `canPlayType`
252
+ * reports `type` unplayable. Falls back to `EspalierVideo.defaultAdapter`.
253
+ */
254
+ adapter: VideoAdapter | null;
255
+ connectedCallback(): void;
256
+ disconnectedCallback(): void;
257
+ protected willUpdate(changedProperties: PropertyValues): void;
258
+ protected updated(changedProperties: PropertyValues): void;
259
+ protected render(): import("lit-html").TemplateResult<1>;
260
+ static styles: import("lit").CSSResult[];
261
+ }
262
+ declare global {
263
+ interface HTMLElementTagNameMap {
264
+ "esp-video": EspalierVideo;
265
+ }
266
+ }
@@ -0,0 +1,253 @@
1
+ var o=function(n,t,e,s){var i=arguments.length,r=i<3?t:s===null?s=Object.getOwnPropertyDescriptor(t,e):s,c;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")r=Reflect.decorate(n,t,e,s);else for(var d=n.length-1;d>=0;d--)(c=n[d])&&(r=(i<3?c(r):i>3?c(t,e,r):c(t,e))||r);return i>3&&r&&Object.defineProperty(t,e,r),r},u;import{css as A,html as h,nothing as p}from"lit";import{customElement as S,property as l,state as f}from"lit/decorators.js";import{keyed as O}from"lit/directives/keyed.js";import{EspalierElementBase as m}from"../shared/esp-element-base.js";import{ESP_EVENTS as y}from"../shared/events.js";import{srOnly as w}from"../shared/style-fragments.js";import{prefersReducedMotion as x}from"../shared/viewport.js";import{parseImageRatio as P}from"../image/image-ratio.js";import{enumConverter as v}from"../shared/enum-converter.js";const g="application/vnd.apple.mpegurl",C="16 / 9",b=new Set(["none","metadata","auto"]),k=new Set(["native","none"]),T=new Set(["","anonymous","use-credentials"]),$={fromAttribute(n){return n===null?"":n==="use-credentials"?"use-credentials":"anonymous"},toAttribute(n){return n||null}};function R(n){return n instanceof Error&&n.message?n.message:typeof n=="string"&&n?n:"The adapter failed to attach the stream."}function E(n){const t=n.error;return t?t.message||`MediaError code ${t.code}.`:"The video reported a playback error."}let a=u=class extends m{constructor(){super(...arguments),this.src="",this.type="application/vnd.apple.mpegurl",this.poster="",this.posterAlt="",this.label="",this.aspectRatio="16 / 9",this.preload="none",this.autoplay=!1,this.muted=!1,this.loop=!1,this.playsinline=!1,this.controls="native",this.deferOffscreen=!1,this.fallbackSrc="",this.crossOrigin="",this.adapter=null,this.phase="idle",this.started=!1,this.offscreenAdmitted=!1,this.attached=null,this.adapterAttaching=!1,this.adapterAttachErrored=!1,this.attaching=null,this.videoKey=0,this.mutedVideo=null,this.attachingEarly=!1,this.teardown=null,this.generation=0,this.triedFallback=!1,this.readyEmitted=!1,this.playIntent=!1,this.autoplayAttempted=!1,this.intentAutoplay=!1,this.autoplayPending=!1,this.playAttempt=0,this.pendingPlay=null,this.focusMessageOnError=!1,this.offscreenObserver=null,this.handleTrackSlotChange=()=>{const t=this.video;t&&this.attached&&this.mirrorTracks(t)},this.handlePlayClick=()=>{this.phase!=="loading"&&this.requestPlayback(!1)},this.handleLoadedMetadata=t=>{this.isCurrent(t)&&this.emitReady(t.currentTarget)},this.handlePlay=t=>{if(!this.isCurrent(t)||!this.attached)return;const e=this.autoplayPending;this.autoplayPending=!1,this.intentAutoplay=e,this.playIntent=!0,this.pendingPlay={autoplay:e}},this.handlePlaying=async t=>{const e=t.currentTarget;if(!this.isCurrent(t)||!this.attached)return;const s=this.pendingPlay;this.pendingPlay=null;const i=this.generation;if(s&&this.dispatchEvent(new CustomEvent(y.VIDEO_PLAY,{detail:{src:this.src,mode:this.attached.mode,currentTime:e.currentTime,autoplay:s.autoplay},bubbles:!0,composed:!0})),i!==this.generation)return;const r=this.shadowRoot?.activeElement?.classList.contains("play")??!1;this.started=!0,this.phase="playing",r&&(await this.updateComplete,e.focus())},this.handleVideoError=t=>{const e=t.currentTarget;if(!this.isCurrent(t))return;if(!this.attached){this.adapterAttaching&&(this.adapterAttachErrored=!0);return}const s=this.generation,i=this.playIntent&&(!this.started||!e.paused);this.failOver(e,"media",E(e))&&i&&this.startPlayback(e,s)}}get video(){return this.shadowRoot?.querySelector("video")??null}canPlayNatively(t){return t.canPlayType(this.type||g)!==""}resolvedAdapter(){return this.adapter??u.defaultAdapter}playLabel(){return this.label?this.label:this.posterAlt?`Play video: ${this.posterAlt}`:"Play video"}connectedCallback(){super.connectedCallback(),this.requestUpdate()}disconnectedCallback(){super.disconnectedCallback(),this.offscreenObserver?.disconnect(),this.offscreenObserver=null,this.offscreenAdmitted=!1,this.resetSource()}syncOffscreenDeferral(){if(!this.deferOffscreen||typeof IntersectionObserver>"u"){this.offscreenObserver?.disconnect(),this.offscreenObserver=null,this.offscreenAdmitted||(this.offscreenAdmitted=!0);return}if(this.offscreenAdmitted||this.offscreenObserver)return;const t=new IntersectionObserver(e=>{this.offscreenObserver===t&&e.some(s=>s.isIntersecting)&&(t.disconnect(),this.offscreenObserver=null,this.offscreenAdmitted=!0)},{rootMargin:"200px"});this.offscreenObserver=t,t.observe(this)}willUpdate(t){super.willUpdate(t),this.isConnected&&!this.offscreenAdmitted&&this.syncOffscreenDeferral(),this.src??="",this.type??=g,this.poster??="",this.posterAlt??="",this.label??="",this.fallbackSrc??="";const e=P(this.aspectRatio)??C;this.style.setProperty("--_esp-video-aspect-ratio",e),t.has("preload")&&!b.has(this.preload)&&(this.preload="none"),t.has("controls")&&!k.has(this.controls)&&(this.controls="native"),t.has("crossOrigin")&&!T.has(this.crossOrigin)&&(this.crossOrigin="anonymous"),this.hasUpdated&&["src","type","fallbackSrc"].some(s=>t.has(s)&&t.get(s)!==this[s])&&this.resetSource()}updated(t){if(super.updated(t),this.syncMuted(t),this.focusMessageOnError&&this.phase==="error"&&(this.focusMessageOnError=!1,this.shadowRoot?.querySelector('[part="message"]')?.focus()),!(!this.offscreenAdmitted||!this.isConnected)){if(this.autoplay&&!this.autoplayAttempted&&!this.playIntent&&(this.src||this.fallbackSrc)&&(this.autoplayAttempted=!0,!x())){this.requestPlayback(!0);return}if(this.preload!=="none"&&this.phase!=="error"&&!this.attached&&!this.attaching&&this.src){const e=this.video;e&&this.ensureSource(e,!0)}}}syncMuted(t){const e=this.video;if(!e)return;const s=e!==this.mutedVideo;this.mutedVideo=e,!(!s&&!t.has("muted")&&!t.has("autoplay"))&&(this.muted||this.autoplay?e.muted=!0:e.paused&&(e.muted=!1))}async requestPlayback(t){if(this.phase==="loading"&&this.playIntent)return;const e=this.generation;this.playIntent=!0,this.intentAutoplay=t,(!this.started||this.phase==="error")&&(this.phase="loading"),await this.updateComplete;const s=this.video;!s||e!==this.generation||!await this.ensureSource(s,!1)||e!==this.generation||(t&&(s.muted=!0),await this.startPlayback(s,e))}async startPlayback(t,e){if(e!==this.generation)return;const s=++this.playAttempt;this.autoplayPending=this.intentAutoplay;try{await t.play()}catch(i){if(e!==this.generation||s!==this.playAttempt)return;this.autoplayPending=!1,i?.name==="NotAllowedError"&&this.phase!=="error"&&(this.playIntent=!1,this.phase=this.started?"playing":"idle")}}ensureSource(t,e){if(this.attached)return Promise.resolve(!0);if(this.attaching&&(e||!this.attachingEarly))return this.attaching;const s=this.generation,i=this.attaching,r=i?i.then(c=>c||s!==this.generation?c:this.attachSource(t,e,s)):this.attachSource(t,e,s);return this.attaching=r,this.attachingEarly=e,r.finally(()=>{this.attaching===r&&(this.attaching=null)}),r}async attachSource(t,e,s){if(!this.src)return e?!1:this.fallbackSrc?(this.useSource(t,this.fallbackSrc,"fallback"),!0):(this.fail(null,"unsupported","No video source is set."),!1);if(this.canPlayNatively(t))return this.useSource(t,this.src,"native"),!0;if(e)return!1;const i=this.resolvedAdapter();if(i){this.mirrorTracks(t);let r;const c=t.error;this.adapterAttaching=!0,this.adapterAttachErrored=!1;try{r=await i(t,this.src)}catch(d){return s!==this.generation?!1:this.failOver(t,"adapter",R(d))}finally{s===this.generation&&(this.adapterAttaching=!1)}return s!==this.generation?(typeof r=="function"&&this.runTeardown(r),!1):(this.teardown=typeof r=="function"?r:null,this.attached={mode:"adapter"},this.readyEmitted=!1,this.adapterAttachErrored||t.error&&t.error!==c?this.failOver(t,"media",E(t)):(t.readyState>=1&&this.emitReady(t),!0))}return this.fallbackSrc?(this.useSource(t,this.fallbackSrc,"fallback"),!0):(this.fail(null,"unsupported",`This browser cannot play ${this.type} and no adapter is set.`),!1)}useSource(t,e,s){this.mirrorTracks(t),this.attached={mode:s},this.readyEmitted=!1,t.src=e}failOver(t,e,s){const i=this.attached?.mode??(e==="adapter"?"adapter":null),r=!!this.fallbackSrc&&!this.triedFallback&&i!=="fallback",c=this.generation;return this.emitError(i,e,s,r),c!==this.generation?!1:(this.runTeardown(),this.attached=null,this.pendingPlay=null,r?(this.triedFallback=!0,this.useSource(t,this.fallbackSrc,"fallback"),!0):(this.enterError(),!1))}emitError(t,e,s,i){this.dispatchEvent(new CustomEvent(y.VIDEO_ERROR,{detail:{src:this.src,mode:t,reason:e,message:s,fallback:i},bubbles:!0,composed:!0}))}fail(t,e,s){const i=this.generation;this.emitError(t,e,s,!1),i===this.generation&&this.enterError()}enterError(){const t=this.shadowRoot?.activeElement;this.focusMessageOnError=!!t?.classList.contains("play")||t!==null&&t===this.video,this.phase="error"}runTeardown(t=this.teardown){t===this.teardown&&(this.teardown=null);try{t?.()}catch{}}resetSource(){this.generation+=1,this.runTeardown();const t=this.attached!==null;this.attached=null,this.attaching&&(this.videoKey+=1,this.requestUpdate()),this.attaching=null,this.adapterAttaching=!1,this.adapterAttachErrored=!1,this.triedFallback=!1,this.readyEmitted=!1,this.playIntent=!1,this.autoplayAttempted=!1,this.intentAutoplay=!1,this.autoplayPending=!1,this.pendingPlay=null,this.focusMessageOnError=!1,this.started=!1,this.phase="idle";const e=this.video;e&&(t||e.hasAttribute("src"))&&(e.pause(),e.removeAttribute("src"),e.load())}mirrorTracks(t){for(const e of t.querySelectorAll("track"))e.remove();for(const e of this.children)e.localName==="track"&&t.append(e.cloneNode(!0))}isCurrent(t){return t.currentTarget===this.video}emitReady(t){!this.attached||this.readyEmitted||(this.readyEmitted=!0,this.dispatchEvent(new CustomEvent(y.VIDEO_READY,{detail:{src:this.src,mode:this.attached.mode,duration:t.duration},bubbles:!0,composed:!0})))}renderPosterContent(){if(this.phase==="error")return h`<div part="message" class="message" role="alert" tabindex="-1">
2
+ <slot name="error">This video can’t be played right now.</slot>
3
+ </div>`;const t=this.phase==="loading";return h`<button
4
+ part="play"
5
+ class="play"
6
+ type="button"
7
+ aria-label=${this.playLabel()}
8
+ aria-disabled=${t?"true":p}
9
+ @click=${this.handlePlayClick}
10
+ >
11
+ <span part="play-icon" class="play-icon" aria-hidden="true">
12
+ ${t?h`<span class="spinner"></span>`:h`<svg viewBox="0 0 24 24" focusable="false">
13
+ <path
14
+ d="M8 5.5v13a1 1 0 0 0 1.52.85l10.4-6.5a1 1 0 0 0 0-1.7L9.52 4.65A1 1 0 0 0 8 5.5Z"
15
+ />
16
+ </svg>`}
17
+ </span>
18
+ </button>`}render(){const t=!this.started||this.phase==="error",e=!this.deferOffscreen||this.offscreenAdmitted;return h`
19
+ <div part="frame" class="frame" data-phase=${this.phase}>
20
+ ${O(this.videoKey,h`<video
21
+ part="video"
22
+ class="video"
23
+ preload=${this.preload}
24
+ crossorigin=${this.crossOrigin||p}
25
+ tabindex=${this.controls==="none"||this.phase==="error"?"-1":p}
26
+ aria-label=${this.label||this.posterAlt||p}
27
+ ?controls=${this.controls==="native"&&this.started&&this.phase!=="error"}
28
+ ?loop=${this.loop}
29
+ ?playsinline=${this.playsinline}
30
+ @loadedmetadata=${this.handleLoadedMetadata}
31
+ @play=${this.handlePlay}
32
+ @playing=${this.handlePlaying}
33
+ @error=${this.handleVideoError}
34
+ ></video>`)}
35
+ <div part="poster" class="poster" ?data-dismissed=${!t}>
36
+ ${this.poster&&e?h`<img
37
+ part="poster-image"
38
+ class="poster-image"
39
+ loading=${this.deferOffscreen?"lazy":"eager"}
40
+ decoding="async"
41
+ alt=""
42
+ src=${this.poster}
43
+ />`:p}
44
+ ${this.renderPosterContent()}
45
+ </div>
46
+ <span class="sr-only" role="status"
47
+ >${this.phase==="loading"?"Loading video":""}</span
48
+ >
49
+ <slot class="tracks" @slotchange=${this.handleTrackSlotChange}></slot>
50
+ </div>
51
+ `}};a.defaultAdapter=null,a.styles=[...m.styles,w,A`
52
+ :host {
53
+ display: block;
54
+ position: relative;
55
+ box-sizing: border-box;
56
+ aspect-ratio: var(--_esp-video-aspect-ratio, 16 / 9);
57
+ overflow: hidden;
58
+ border-radius: var(--esp-video-border-radius, var(--esp-size-border-radius));
59
+ background: var(
60
+ --esp-video-surface,
61
+ oklch(from var(--esp-color-text, oklch(0.2 0 0)) 0.16 min(c, 0.02) h)
62
+ );
63
+ }
64
+
65
+ .frame,
66
+ .video {
67
+ display: block;
68
+ inline-size: 100%;
69
+ block-size: 100%;
70
+ }
71
+
72
+ .frame {
73
+ position: relative;
74
+ }
75
+
76
+ .video {
77
+ object-fit: var(--esp-video-object-fit, contain);
78
+ accent-color: var(--esp-video-accent, var(--esp-color-primary));
79
+ }
80
+
81
+ .video:focus {
82
+ outline: none;
83
+ }
84
+
85
+ .video:focus-visible {
86
+ outline: var(--esp-video-focus-outline, 3px solid var(--esp-color-link));
87
+ outline-offset: -3px;
88
+ }
89
+
90
+ .poster {
91
+ position: absolute;
92
+ inset: 0;
93
+ display: grid;
94
+ place-items: center;
95
+ transition:
96
+ opacity 200ms ease,
97
+ visibility 0s linear 0s;
98
+ }
99
+
100
+ .poster[data-dismissed] {
101
+ opacity: 0;
102
+ visibility: hidden;
103
+ pointer-events: none;
104
+ transition:
105
+ opacity 200ms ease,
106
+ visibility 0s linear 200ms;
107
+ }
108
+
109
+ .poster-image {
110
+ position: absolute;
111
+ inset: 0;
112
+ display: block;
113
+ inline-size: 100%;
114
+ block-size: 100%;
115
+ object-fit: cover;
116
+ }
117
+
118
+ .frame[data-phase="error"] .poster-image {
119
+ opacity: 0.45;
120
+ }
121
+
122
+ .play {
123
+ position: absolute;
124
+ inset: 0;
125
+ display: grid;
126
+ place-items: center;
127
+ margin: 0;
128
+ padding: 0;
129
+ border: 0;
130
+ background: transparent;
131
+ color: inherit;
132
+ font: inherit;
133
+ cursor: pointer;
134
+ -webkit-tap-highlight-color: transparent;
135
+ }
136
+
137
+ .play[aria-disabled="true"] {
138
+ cursor: progress;
139
+ }
140
+
141
+ .play:focus {
142
+ outline: none;
143
+ }
144
+
145
+ .play-icon {
146
+ display: grid;
147
+ place-items: center;
148
+ inline-size: var(--esp-video-play-size, var(--esp-size-huge, 4rem));
149
+ aspect-ratio: 1;
150
+ border-radius: 50%;
151
+ background: var(
152
+ --esp-video-play-background,
153
+ color-mix(in oklch, var(--esp-color-background, oklch(0.98 0 0)) 90%, transparent)
154
+ );
155
+ color: var(--esp-video-play-color, var(--esp-color-text, oklch(0.2 0 0)));
156
+
157
+ box-shadow:
158
+ 0 0 0 1px
159
+ color-mix(
160
+ in oklch,
161
+ var(--esp-video-play-color, var(--esp-color-text, oklch(0.2 0 0))) 28%,
162
+ transparent
163
+ ),
164
+ 0 2px 12px oklch(0 0 0 / 0.28);
165
+ transition: transform 150ms ease;
166
+ }
167
+
168
+ .play:hover .play-icon {
169
+ transform: scale(1.06);
170
+ }
171
+
172
+ .play:focus-visible .play-icon {
173
+ outline: var(--esp-video-focus-outline, 3px solid var(--esp-color-link));
174
+ outline-offset: 3px;
175
+ }
176
+
177
+ .play-icon svg {
178
+ inline-size: 45%;
179
+ block-size: 45%;
180
+
181
+ translate: 6% 0;
182
+ fill: currentColor;
183
+ }
184
+
185
+ .spinner {
186
+ inline-size: 40%;
187
+ aspect-ratio: 1;
188
+ border-radius: 50%;
189
+ border: 3px solid currentColor;
190
+ border-block-start-color: transparent;
191
+ animation: esp-video-spin 900ms linear infinite;
192
+ }
193
+
194
+ @keyframes esp-video-spin {
195
+ to {
196
+ rotate: 1turn;
197
+ }
198
+ }
199
+
200
+ .message {
201
+ position: relative;
202
+ max-inline-size: min(28rem, calc(100% - 2 * var(--esp-size-padding, 1rem)));
203
+ padding: var(--esp-size-small, 0.5rem) var(--esp-size-padding, 1rem);
204
+ border-radius: var(--esp-size-border-radius);
205
+ background: var(--esp-video-message-background, var(--esp-color-background));
206
+ color: var(--esp-video-message-color, var(--esp-color-text));
207
+ font-size: var(--esp-type-small, 0.875rem);
208
+ text-align: center;
209
+ }
210
+
211
+ .message:focus {
212
+ outline: none;
213
+ }
214
+
215
+ .message:focus-visible {
216
+ outline: var(--esp-video-focus-outline, 3px solid var(--esp-color-link));
217
+ outline-offset: 2px;
218
+ }
219
+
220
+ .tracks {
221
+ display: none;
222
+ }
223
+
224
+ @media (prefers-reduced-motion: reduce) {
225
+ .poster,
226
+ .poster[data-dismissed],
227
+ .play-icon {
228
+ transition: none;
229
+ }
230
+ .play:hover .play-icon {
231
+ transform: none;
232
+ }
233
+ .spinner {
234
+ animation: none;
235
+ border-block-start-color: currentColor;
236
+ opacity: 0.6;
237
+ }
238
+ }
239
+
240
+ @media (forced-colors: active) {
241
+ .play-icon {
242
+ border: 2px solid ButtonText;
243
+ background: ButtonFace;
244
+ color: ButtonText;
245
+ }
246
+ .play:focus-visible .play-icon {
247
+ outline-color: Highlight;
248
+ }
249
+ .message {
250
+ border: 1px solid CanvasText;
251
+ }
252
+ }
253
+ `],o([l({type:String})],a.prototype,"src",void 0),o([l({type:String})],a.prototype,"type",void 0),o([l({type:String})],a.prototype,"poster",void 0),o([l({attribute:"poster-alt",type:String})],a.prototype,"posterAlt",void 0),o([l({type:String})],a.prototype,"label",void 0),o([l({attribute:"aspect-ratio",type:String})],a.prototype,"aspectRatio",void 0),o([l({converter:v(b,"none"),reflect:!0})],a.prototype,"preload",void 0),o([l({type:Boolean})],a.prototype,"autoplay",void 0),o([l({type:Boolean})],a.prototype,"muted",void 0),o([l({type:Boolean})],a.prototype,"loop",void 0),o([l({type:Boolean})],a.prototype,"playsinline",void 0),o([l({converter:v(k,"native"),reflect:!0})],a.prototype,"controls",void 0),o([l({attribute:"defer-offscreen",type:Boolean})],a.prototype,"deferOffscreen",void 0),o([l({attribute:"fallback-src",type:String})],a.prototype,"fallbackSrc",void 0),o([l({attribute:"crossorigin",converter:$})],a.prototype,"crossOrigin",void 0),o([l({attribute:!1})],a.prototype,"adapter",void 0),o([f()],a.prototype,"phase",void 0),o([f()],a.prototype,"started",void 0),o([f()],a.prototype,"offscreenAdmitted",void 0),a=u=o([S("esp-video")],a);export{C as DEFAULT_VIDEO_ASPECT_RATIO,a as EspalierVideo,g as HLS_MEDIA_TYPE};