@taprootio/espalier 4.20.1 → 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.
- package/CHANGELOG.md +32 -0
- package/custom-elements.json +5095 -2892
- package/dist/checkbox/esp-checkbox-group.d.ts +6 -0
- package/dist/checkbox/esp-checkbox-group.js +10 -5
- package/dist/checkbox/esp-checkbox.d.ts +24 -0
- package/dist/checkbox/esp-checkbox.js +3 -3
- package/dist/cli/theme-check.js +2 -2
- package/dist/date-picker/esp-date-picker.js +6 -6
- package/dist/details/esp-details.js +8 -2
- package/dist/flyout/esp-flyout.d.ts +1 -1
- package/dist/flyout/esp-flyout.js +3 -9
- package/dist/font-picker/esp-font-picker.js +3 -3
- package/dist/footer/esp-footer.d.ts +1 -1
- package/dist/form-item/esp-form-item.d.ts +35 -0
- package/dist/form-item/esp-form-item.js +16 -11
- package/dist/image/esp-image.d.ts +2 -2
- package/dist/image/esp-image.js +4 -4
- package/dist/image/image-ratio.d.ts +5 -0
- package/dist/image/image-ratio.js +1 -0
- package/dist/image-picker/esp-image-picker.js +2 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/input/esp-input.d.ts +29 -3
- package/dist/input/esp-input.js +14 -14
- package/dist/menu/esp-menu.d.ts +0 -5
- package/dist/menu/esp-menu.js +4 -4
- package/dist/page/esp-page.d.ts +2 -17
- package/dist/page/esp-page.js +2 -2
- package/dist/page/esp-page.styles.js +8 -32
- package/dist/pickers/esp-pick-one.js +7 -7
- package/dist/pickers/esp-pick-some.js +8 -8
- package/dist/pickers/esp-picker-base.js +2 -2
- package/dist/pickers/esp-picker-menu.js +2 -2
- package/dist/radio-button/esp-radio-button-group.d.ts +6 -0
- package/dist/radio-button/esp-radio-button-group.js +5 -4
- package/dist/radio-button/esp-radio-button.d.ts +22 -0
- package/dist/radio-button/esp-radio-button.js +2 -2
- package/dist/root/esp-root.js +2 -2
- package/dist/shared/enum-converter.js +1 -0
- package/dist/shared/events.d.ts +11 -0
- package/dist/shared/events.js +1 -1
- package/dist/shared/font-helpers.d.ts +0 -2
- package/dist/shared/font-helpers.js +1 -1
- package/dist/shared/form-field-description-controller.js +1 -1
- package/dist/shared/form-item-relay.js +1 -0
- package/dist/shared/justified-layout.d.ts +5 -31
- package/dist/shared/justified-layout.js +1 -1
- package/dist/slider/esp-slider.js +1 -1
- package/dist/switch/esp-switch.js +3 -3
- package/dist/textarea/esp-textarea.js +2 -2
- package/dist/video/esp-video.d.ts +266 -0
- package/dist/video/esp-video.js +253 -0
- package/espalier.css-data.json +40 -16
- package/espalier.token-manifest.json +60 -24
- package/licenses/ASSET_PROVENANCE.md +2 -0
- package/licenses/THIRD_PARTY_NOTICES.md +9 -0
- package/licenses/asset-provenance.json +64 -1
- package/package.json +5 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var
|
|
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,
|
|
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
|
-
`],
|
|
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
|
|
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=[...
|
|
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};
|