@videojs/react 10.0.0-beta.29 → 10.0.0-beta.30
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/dist/default/index.js +2 -1
- package/dist/default/media/shaka-video/index.js +2 -0
- package/dist/default/media/shaka-video/media.js +21 -0
- package/dist/default/media/shaka-video/media.js.map +1 -0
- package/dist/default/presets/audio/minimal-skin.css +1 -3
- package/dist/default/presets/audio/skin.css +1 -3
- package/dist/default/presets/live-audio/minimal-skin.css +1 -3
- package/dist/default/presets/live-audio/skin.css +1 -3
- package/dist/default/presets/live-video/minimal-skin.css +2 -31
- package/dist/default/presets/live-video/minimal-skin.js +2 -6
- package/dist/default/presets/live-video/minimal-skin.js.map +1 -1
- package/dist/default/presets/live-video/minimal-skin.tailwind.js +2 -6
- package/dist/default/presets/live-video/minimal-skin.tailwind.js.map +1 -1
- package/dist/default/presets/live-video/skin.css +2 -33
- package/dist/default/presets/live-video/skin.js +2 -6
- package/dist/default/presets/live-video/skin.js.map +1 -1
- package/dist/default/presets/live-video/skin.tailwind.js +2 -6
- package/dist/default/presets/live-video/skin.tailwind.js.map +1 -1
- package/dist/default/presets/video/minimal-skin.css +2 -31
- package/dist/default/presets/video/minimal-skin.js +2 -6
- package/dist/default/presets/video/minimal-skin.js.map +1 -1
- package/dist/default/presets/video/minimal-skin.tailwind.js +2 -6
- package/dist/default/presets/video/minimal-skin.tailwind.js.map +1 -1
- package/dist/default/presets/video/skin.css +2 -33
- package/dist/default/presets/video/skin.js +2 -6
- package/dist/default/presets/video/skin.js.map +1 -1
- package/dist/default/presets/video/skin.tailwind.js +2 -6
- package/dist/default/presets/video/skin.tailwind.js.map +1 -1
- package/dist/default/skins/dist/default/default/tailwind/components/button.js +1 -1
- package/dist/default/skins/dist/default/default/tailwind/components/button.js.map +1 -1
- package/dist/default/skins/dist/default/default/tailwind/components/menu.js +2 -2
- package/dist/default/skins/dist/default/default/tailwind/components/menu.js.map +1 -1
- package/dist/default/skins/dist/default/default/tailwind/components/poster.js +1 -7
- package/dist/default/skins/dist/default/default/tailwind/components/poster.js.map +1 -1
- package/dist/default/skins/dist/default/default/tailwind/video.tailwind.js +1 -10
- package/dist/default/skins/dist/default/default/tailwind/video.tailwind.js.map +1 -1
- package/dist/default/skins/dist/default/minimal/tailwind/components/button.js +1 -1
- package/dist/default/skins/dist/default/minimal/tailwind/components/button.js.map +1 -1
- package/dist/default/skins/dist/default/minimal/tailwind/components/menu.js +2 -2
- package/dist/default/skins/dist/default/minimal/tailwind/components/menu.js.map +1 -1
- package/dist/default/skins/dist/default/minimal/tailwind/components/poster.js +1 -7
- package/dist/default/skins/dist/default/minimal/tailwind/components/poster.js.map +1 -1
- package/dist/default/skins/dist/default/minimal/tailwind/video.tailwind.js +1 -10
- package/dist/default/skins/dist/default/minimal/tailwind/video.tailwind.js.map +1 -1
- package/dist/default/ui/thumbnail/thumbnail.js +18 -1
- package/dist/default/ui/thumbnail/thumbnail.js.map +1 -1
- package/dist/default/ui/title/index.js +2 -0
- package/dist/default/ui/title/title.js +44 -0
- package/dist/default/ui/title/title.js.map +1 -0
- package/dist/dev/index.d.ts +3 -2
- package/dist/dev/index.js +2 -1
- package/dist/dev/media/shaka-video/index.d.ts +2 -0
- package/dist/dev/media/shaka-video/index.js +2 -0
- package/dist/dev/media/shaka-video/media.d.ts +13 -0
- package/dist/dev/media/shaka-video/media.d.ts.map +1 -0
- package/dist/dev/media/shaka-video/media.js +21 -0
- package/dist/dev/media/shaka-video/media.js.map +1 -0
- package/dist/dev/presets/audio/minimal-skin.css +1 -3
- package/dist/dev/presets/audio/skin.css +1 -3
- package/dist/dev/presets/live-audio/minimal-skin.css +1 -3
- package/dist/dev/presets/live-audio/skin.css +1 -3
- package/dist/dev/presets/live-video/minimal-skin.css +2 -31
- package/dist/dev/presets/live-video/minimal-skin.js +2 -6
- package/dist/dev/presets/live-video/minimal-skin.js.map +1 -1
- package/dist/dev/presets/live-video/minimal-skin.tailwind.js +2 -6
- package/dist/dev/presets/live-video/minimal-skin.tailwind.js.map +1 -1
- package/dist/dev/presets/live-video/skin.css +2 -33
- package/dist/dev/presets/live-video/skin.js +2 -6
- package/dist/dev/presets/live-video/skin.js.map +1 -1
- package/dist/dev/presets/live-video/skin.tailwind.js +2 -6
- package/dist/dev/presets/live-video/skin.tailwind.js.map +1 -1
- package/dist/dev/presets/types.d.ts +0 -2
- package/dist/dev/presets/types.d.ts.map +1 -1
- package/dist/dev/presets/video/minimal-skin.css +2 -31
- package/dist/dev/presets/video/minimal-skin.js +2 -6
- package/dist/dev/presets/video/minimal-skin.js.map +1 -1
- package/dist/dev/presets/video/minimal-skin.tailwind.js +2 -6
- package/dist/dev/presets/video/minimal-skin.tailwind.js.map +1 -1
- package/dist/dev/presets/video/skin.css +2 -33
- package/dist/dev/presets/video/skin.js +2 -6
- package/dist/dev/presets/video/skin.js.map +1 -1
- package/dist/dev/presets/video/skin.tailwind.js +2 -6
- package/dist/dev/presets/video/skin.tailwind.js.map +1 -1
- package/dist/dev/skins/dist/default/default/tailwind/components/button.js +1 -1
- package/dist/dev/skins/dist/default/default/tailwind/components/button.js.map +1 -1
- package/dist/dev/skins/dist/default/default/tailwind/components/menu.js +2 -2
- package/dist/dev/skins/dist/default/default/tailwind/components/menu.js.map +1 -1
- package/dist/dev/skins/dist/default/default/tailwind/components/poster.js +1 -7
- package/dist/dev/skins/dist/default/default/tailwind/components/poster.js.map +1 -1
- package/dist/dev/skins/dist/default/default/tailwind/video.tailwind.js +1 -10
- package/dist/dev/skins/dist/default/default/tailwind/video.tailwind.js.map +1 -1
- package/dist/dev/skins/dist/default/minimal/tailwind/components/button.js +1 -1
- package/dist/dev/skins/dist/default/minimal/tailwind/components/button.js.map +1 -1
- package/dist/dev/skins/dist/default/minimal/tailwind/components/menu.js +2 -2
- package/dist/dev/skins/dist/default/minimal/tailwind/components/menu.js.map +1 -1
- package/dist/dev/skins/dist/default/minimal/tailwind/components/poster.js +1 -7
- package/dist/dev/skins/dist/default/minimal/tailwind/components/poster.js.map +1 -1
- package/dist/dev/skins/dist/default/minimal/tailwind/video.tailwind.js +1 -10
- package/dist/dev/skins/dist/default/minimal/tailwind/video.tailwind.js.map +1 -1
- package/dist/dev/ui/thumbnail/thumbnail.d.ts.map +1 -1
- package/dist/dev/ui/thumbnail/thumbnail.js +18 -1
- package/dist/dev/ui/thumbnail/thumbnail.js.map +1 -1
- package/dist/dev/ui/title/index.d.ts +2 -0
- package/dist/dev/ui/title/index.js +2 -0
- package/dist/dev/ui/title/title.d.ts +27 -0
- package/dist/dev/ui/title/title.d.ts.map +1 -0
- package/dist/dev/ui/title/title.js +47 -0
- package/dist/dev/ui/title/title.js.map +1 -0
- package/docs/README.md +1 -1
- package/docs/concepts/presets.md +4 -4
- package/docs/concepts/skins.md +8 -48
- package/docs/how-to/add-a-poster-placeholder.md +133 -0
- package/docs/how-to/customize-skins.md +10 -92
- package/docs/how-to/migrate-from-mux-player.md +14 -8
- package/docs/llms.txt +3 -0
- package/docs/reference/feature-fullscreen.md +9 -8
- package/docs/reference/feature-metadata.md +129 -0
- package/docs/reference/feature-orientation-lock.md +94 -8
- package/docs/reference/feature-text-tracks.md +10 -0
- package/docs/reference/menu.md +0 -1
- package/docs/reference/thumbnail.md +4 -0
- package/docs/reference/title.md +208 -0
- package/package.json +8 -8
|
@@ -383,7 +383,6 @@ export interface VideoPlayerProps {
|
|
|
383
383
|
style?: CSSProperties;
|
|
384
384
|
className?: string;
|
|
385
385
|
renderPoster?: Poster.Props['render'];
|
|
386
|
-
placeholder?: string | undefined;
|
|
387
386
|
}
|
|
388
387
|
|
|
389
388
|
/**
|
|
@@ -395,18 +394,10 @@ export interface VideoPlayerProps {
|
|
|
395
394
|
* />
|
|
396
395
|
* ```
|
|
397
396
|
*/
|
|
398
|
-
export function VideoPlayer({ src, poster, className, renderPoster,
|
|
399
|
-
const containerStyle = placeholder
|
|
400
|
-
? ({ '--media-poster-placeholder': `url(${placeholder})`, ...style } as CSSProperties)
|
|
401
|
-
: style;
|
|
402
|
-
|
|
397
|
+
export function VideoPlayer({ src, poster, className, renderPoster, style, ...rest }: VideoPlayerProps): ReactNode {
|
|
403
398
|
return (
|
|
404
399
|
<Player poster={poster}>
|
|
405
|
-
<Container
|
|
406
|
-
className={`media-default-skin media-default-skin--video ${className ?? ''}`}
|
|
407
|
-
style={containerStyle}
|
|
408
|
-
{...rest}
|
|
409
|
-
>
|
|
400
|
+
<Container className={`media-default-skin media-default-skin--video ${className ?? ''}`} style={style} {...rest}>
|
|
410
401
|
<Video src={src} playsInline />
|
|
411
402
|
|
|
412
403
|
<Poster render={renderPoster} />
|
|
@@ -1095,7 +1086,7 @@ export const { Player } = createPlayer({ features: videoFeatures });
|
|
|
1095
1086
|
@media (prefers-reduced-motion: reduce) {
|
|
1096
1087
|
.media-default-skin .media-button {
|
|
1097
1088
|
scale: 1;
|
|
1098
|
-
transition-property: background-color, color
|
|
1089
|
+
transition-property: background-color, color;
|
|
1099
1090
|
will-change: auto;
|
|
1100
1091
|
}
|
|
1101
1092
|
}
|
|
@@ -1140,8 +1131,7 @@ export const { Player } = createPlayer({ features: videoFeatures });
|
|
|
1140
1131
|
transition: opacity 0.25s;
|
|
1141
1132
|
}
|
|
1142
1133
|
.media-default-skin media-poster:not([data-visible]),
|
|
1143
|
-
.media-default-skin > img:not([data-visible])
|
|
1144
|
-
.media-default-skin > img[data-visible]:not([data-loaded]) {
|
|
1134
|
+
.media-default-skin > img:not([data-visible]) {
|
|
1145
1135
|
opacity: 0;
|
|
1146
1136
|
}
|
|
1147
1137
|
.media-default-skin media-poster ::slotted(img),
|
|
@@ -1160,30 +1150,6 @@ export const { Player } = createPlayer({ features: videoFeatures });
|
|
|
1160
1150
|
border-radius: inherit;
|
|
1161
1151
|
}
|
|
1162
1152
|
|
|
1163
|
-
/* Blurred placeholder: HTML path targets media-poster::before, React path targets the skin container::before */
|
|
1164
|
-
.media-default-skin media-poster::before,
|
|
1165
|
-
.media-default-skin::before {
|
|
1166
|
-
position: absolute;
|
|
1167
|
-
inset: 0;
|
|
1168
|
-
pointer-events: none;
|
|
1169
|
-
content: "";
|
|
1170
|
-
background-image: var(--media-poster-placeholder, none);
|
|
1171
|
-
background-repeat: no-repeat;
|
|
1172
|
-
background-position: var(--media-object-position, center);
|
|
1173
|
-
background-size: var(--media-object-fit, contain);
|
|
1174
|
-
filter: blur(var(--media-poster-placeholder-blur, 20px));
|
|
1175
|
-
}
|
|
1176
|
-
|
|
1177
|
-
/* React path: hidden by default, transitions in while poster loads */
|
|
1178
|
-
.media-default-skin::before {
|
|
1179
|
-
opacity: 0;
|
|
1180
|
-
transition: opacity 0.25s;
|
|
1181
|
-
}
|
|
1182
|
-
/* Show placeholder while the poster is visible but not yet loaded */
|
|
1183
|
-
.media-default-skin:has(img[data-visible]:not([data-loaded]))::before {
|
|
1184
|
-
opacity: 1;
|
|
1185
|
-
}
|
|
1186
|
-
|
|
1187
1153
|
.media-default-skin:fullscreen media-poster ::slotted(img),
|
|
1188
1154
|
.media-default-skin:fullscreen media-poster img,
|
|
1189
1155
|
.media-default-skin:fullscreen > img {
|
|
@@ -1788,7 +1754,6 @@ export const { Player } = createPlayer({ features: videoFeatures });
|
|
|
1788
1754
|
transition-timing-function: ease-out;
|
|
1789
1755
|
transition-duration: var(--menu-transition-duration);
|
|
1790
1756
|
transition-property: translate, filter;
|
|
1791
|
-
will-change: translate, filter;
|
|
1792
1757
|
|
|
1793
1758
|
&:where([data-starting-style], [data-ending-style]) {
|
|
1794
1759
|
overflow: hidden;
|
|
@@ -1972,7 +1937,6 @@ export const { Player } = createPlayer({ features: videoFeatures });
|
|
|
1972
1937
|
transition:
|
|
1973
1938
|
translate var(--menu-transition-duration) ease-out,
|
|
1974
1939
|
filter var(--menu-transition-duration) ease-out;
|
|
1975
|
-
will-change: translate, filter;
|
|
1976
1940
|
}
|
|
1977
1941
|
|
|
1978
1942
|
&[data-submenu-expanded="true"] > :not([data-submenu]) {
|
|
@@ -2526,12 +2490,10 @@ export const { Player } = createPlayer({ features: videoFeatures });
|
|
|
2526
2490
|
|
|
2527
2491
|
@media (pointer: fine) {
|
|
2528
2492
|
transition-property: filter, opacity, scale, translate;
|
|
2529
|
-
will-change: filter, opacity, scale, translate;
|
|
2530
2493
|
}
|
|
2531
2494
|
|
|
2532
2495
|
@media (pointer: coarse) {
|
|
2533
2496
|
transition-property: opacity, scale, translate;
|
|
2534
|
-
will-change: opacity, scale, translate;
|
|
2535
2497
|
}
|
|
2536
2498
|
}
|
|
2537
2499
|
|
|
@@ -2616,12 +2578,10 @@ export const { Player } = createPlayer({ features: videoFeatures });
|
|
|
2616
2578
|
|
|
2617
2579
|
@media (pointer: fine) {
|
|
2618
2580
|
transition-property: filter, opacity, scale, translate;
|
|
2619
|
-
will-change: filter, opacity, scale, translate;
|
|
2620
2581
|
}
|
|
2621
2582
|
|
|
2622
2583
|
@media (pointer: coarse) {
|
|
2623
2584
|
transition-property: opacity, scale, translate;
|
|
2624
|
-
will-change: opacity, scale, translate;
|
|
2625
2585
|
}
|
|
2626
2586
|
|
|
2627
2587
|
@container media-root (width > 42rem) {
|
|
@@ -3302,7 +3262,7 @@ export const { Player } = createPlayer({ features: audioFeatures });
|
|
|
3302
3262
|
@media (prefers-reduced-motion: reduce) {
|
|
3303
3263
|
.media-default-skin .media-button {
|
|
3304
3264
|
scale: 1;
|
|
3305
|
-
transition-property: background-color, color
|
|
3265
|
+
transition-property: background-color, color;
|
|
3306
3266
|
will-change: auto;
|
|
3307
3267
|
}
|
|
3308
3268
|
}
|
|
@@ -3874,7 +3834,6 @@ export const { Player } = createPlayer({ features: audioFeatures });
|
|
|
3874
3834
|
transition-timing-function: ease-out;
|
|
3875
3835
|
transition-duration: var(--menu-transition-duration);
|
|
3876
3836
|
transition-property: translate, filter;
|
|
3877
|
-
will-change: translate, filter;
|
|
3878
3837
|
|
|
3879
3838
|
&:where([data-starting-style], [data-ending-style]) {
|
|
3880
3839
|
overflow: hidden;
|
|
@@ -4058,7 +4017,6 @@ export const { Player } = createPlayer({ features: audioFeatures });
|
|
|
4058
4017
|
transition:
|
|
4059
4018
|
translate var(--menu-transition-duration) ease-out,
|
|
4060
4019
|
filter var(--menu-transition-duration) ease-out;
|
|
4061
|
-
will-change: translate, filter;
|
|
4062
4020
|
}
|
|
4063
4021
|
|
|
4064
4022
|
&[data-submenu-expanded="true"] > :not([data-submenu]) {
|
|
@@ -4457,7 +4415,6 @@ export interface VideoPlayerProps {
|
|
|
4457
4415
|
style?: CSSProperties;
|
|
4458
4416
|
className?: string;
|
|
4459
4417
|
renderPoster?: Poster.Props['render'];
|
|
4460
|
-
placeholder?: string | undefined;
|
|
4461
4418
|
}
|
|
4462
4419
|
|
|
4463
4420
|
/**
|
|
@@ -4469,18 +4426,10 @@ export interface VideoPlayerProps {
|
|
|
4469
4426
|
* />
|
|
4470
4427
|
* ```
|
|
4471
4428
|
*/
|
|
4472
|
-
export function VideoPlayer({ src, poster, className, renderPoster,
|
|
4473
|
-
const containerStyle = placeholder
|
|
4474
|
-
? ({ '--media-poster-placeholder': `url(${placeholder})`, ...style } as CSSProperties)
|
|
4475
|
-
: style;
|
|
4476
|
-
|
|
4429
|
+
export function VideoPlayer({ src, poster, className, renderPoster, style, ...rest }: VideoPlayerProps): ReactNode {
|
|
4477
4430
|
return (
|
|
4478
4431
|
<Player poster={poster}>
|
|
4479
|
-
<Container
|
|
4480
|
-
className={`media-minimal-skin media-minimal-skin--video ${className ?? ''}`}
|
|
4481
|
-
style={containerStyle}
|
|
4482
|
-
{...rest}
|
|
4483
|
-
>
|
|
4432
|
+
<Container className={`media-minimal-skin media-minimal-skin--video ${className ?? ''}`} style={style} {...rest}>
|
|
4484
4433
|
<Video src={src} playsInline />
|
|
4485
4434
|
|
|
4486
4435
|
<Poster render={renderPoster} />
|
|
@@ -5237,7 +5186,7 @@ export const { Player } = createPlayer({ features: videoFeatures });
|
|
|
5237
5186
|
@media (prefers-reduced-motion: reduce) {
|
|
5238
5187
|
.media-minimal-skin .media-button {
|
|
5239
5188
|
scale: 1;
|
|
5240
|
-
transition-property: background-color
|
|
5189
|
+
transition-property: background-color;
|
|
5241
5190
|
will-change: auto;
|
|
5242
5191
|
}
|
|
5243
5192
|
}
|
|
@@ -5282,8 +5231,7 @@ export const { Player } = createPlayer({ features: videoFeatures });
|
|
|
5282
5231
|
transition: opacity 0.25s;
|
|
5283
5232
|
}
|
|
5284
5233
|
.media-minimal-skin media-poster:not([data-visible]),
|
|
5285
|
-
.media-minimal-skin > img:not([data-visible])
|
|
5286
|
-
.media-minimal-skin > img[data-visible]:not([data-loaded]) {
|
|
5234
|
+
.media-minimal-skin > img:not([data-visible]) {
|
|
5287
5235
|
opacity: 0;
|
|
5288
5236
|
}
|
|
5289
5237
|
.media-minimal-skin media-poster ::slotted(img),
|
|
@@ -5302,30 +5250,6 @@ export const { Player } = createPlayer({ features: videoFeatures });
|
|
|
5302
5250
|
border-radius: inherit;
|
|
5303
5251
|
}
|
|
5304
5252
|
|
|
5305
|
-
/* Blurred placeholder: HTML path targets media-poster::before, React path targets the skin container::before */
|
|
5306
|
-
.media-minimal-skin media-poster::before,
|
|
5307
|
-
.media-minimal-skin::before {
|
|
5308
|
-
position: absolute;
|
|
5309
|
-
inset: 0;
|
|
5310
|
-
pointer-events: none;
|
|
5311
|
-
content: "";
|
|
5312
|
-
background-image: var(--media-poster-placeholder, none);
|
|
5313
|
-
background-repeat: no-repeat;
|
|
5314
|
-
background-position: var(--media-object-position, center);
|
|
5315
|
-
background-size: var(--media-object-fit, contain);
|
|
5316
|
-
filter: blur(var(--media-poster-placeholder-blur, 20px));
|
|
5317
|
-
}
|
|
5318
|
-
|
|
5319
|
-
/* React path: hidden by default, transitions in while poster loads */
|
|
5320
|
-
.media-minimal-skin::before {
|
|
5321
|
-
opacity: 0;
|
|
5322
|
-
transition: opacity 0.25s;
|
|
5323
|
-
}
|
|
5324
|
-
/* Show placeholder while the poster is visible but not yet loaded */
|
|
5325
|
-
.media-minimal-skin:has(img[data-visible]:not([data-loaded]))::before {
|
|
5326
|
-
opacity: 1;
|
|
5327
|
-
}
|
|
5328
|
-
|
|
5329
5253
|
.media-minimal-skin:fullscreen media-poster ::slotted(img),
|
|
5330
5254
|
.media-minimal-skin:fullscreen media-poster img,
|
|
5331
5255
|
.media-minimal-skin:fullscreen > img {
|
|
@@ -5932,7 +5856,6 @@ export const { Player } = createPlayer({ features: videoFeatures });
|
|
|
5932
5856
|
transition-timing-function: ease-out;
|
|
5933
5857
|
transition-duration: var(--menu-transition-duration);
|
|
5934
5858
|
transition-property: translate, filter;
|
|
5935
|
-
will-change: translate, filter;
|
|
5936
5859
|
|
|
5937
5860
|
&:where([data-starting-style], [data-ending-style]) {
|
|
5938
5861
|
overflow: hidden;
|
|
@@ -6111,7 +6034,6 @@ export const { Player } = createPlayer({ features: videoFeatures });
|
|
|
6111
6034
|
transition:
|
|
6112
6035
|
translate var(--menu-transition-duration) ease-out,
|
|
6113
6036
|
filter var(--menu-transition-duration) ease-out;
|
|
6114
|
-
will-change: translate, filter;
|
|
6115
6037
|
}
|
|
6116
6038
|
|
|
6117
6039
|
&[data-submenu-expanded="true"] > :not([data-submenu]) {
|
|
@@ -6667,12 +6589,10 @@ export const { Player } = createPlayer({ features: videoFeatures });
|
|
|
6667
6589
|
|
|
6668
6590
|
@media (pointer: fine) {
|
|
6669
6591
|
transition-property: translate, filter, opacity;
|
|
6670
|
-
will-change: translate, filter, opacity;
|
|
6671
6592
|
}
|
|
6672
6593
|
|
|
6673
6594
|
@media (pointer: coarse) {
|
|
6674
6595
|
transition-property: translate, opacity;
|
|
6675
|
-
will-change: translate, opacity;
|
|
6676
6596
|
}
|
|
6677
6597
|
|
|
6678
6598
|
&:not([data-visible]) {
|
|
@@ -7460,7 +7380,7 @@ export const { Player } = createPlayer({ features: audioFeatures });
|
|
|
7460
7380
|
@media (prefers-reduced-motion: reduce) {
|
|
7461
7381
|
.media-minimal-skin .media-button {
|
|
7462
7382
|
scale: 1;
|
|
7463
|
-
transition-property: background-color
|
|
7383
|
+
transition-property: background-color;
|
|
7464
7384
|
will-change: auto;
|
|
7465
7385
|
}
|
|
7466
7386
|
}
|
|
@@ -8031,7 +7951,6 @@ export const { Player } = createPlayer({ features: audioFeatures });
|
|
|
8031
7951
|
transition-timing-function: ease-out;
|
|
8032
7952
|
transition-duration: var(--menu-transition-duration);
|
|
8033
7953
|
transition-property: translate, filter;
|
|
8034
|
-
will-change: translate, filter;
|
|
8035
7954
|
|
|
8036
7955
|
&:where([data-starting-style], [data-ending-style]) {
|
|
8037
7956
|
overflow: hidden;
|
|
@@ -8210,7 +8129,6 @@ export const { Player } = createPlayer({ features: audioFeatures });
|
|
|
8210
8129
|
transition:
|
|
8211
8130
|
translate var(--menu-transition-duration) ease-out,
|
|
8212
8131
|
filter var(--menu-transition-duration) ease-out;
|
|
8213
|
-
will-change: translate, filter;
|
|
8214
8132
|
}
|
|
8215
8133
|
|
|
8216
8134
|
&[data-submenu-expanded="true"] > :not([data-submenu]) {
|
|
@@ -243,17 +243,23 @@ The poster is a skin concern. Build an `image.mux.com` URL and hand it over:
|
|
|
243
243
|
jsx
|
|
244
244
|
|
|
245
245
|
```
|
|
246
|
-
<
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
246
|
+
<VideoPlayer poster="https://image.mux.com/EcHgOK9coz5K…/thumbnail.webp?time=2">
|
|
247
|
+
<VideoSkin
|
|
248
|
+
renderPoster={
|
|
249
|
+
<img
|
|
250
|
+
alt=""
|
|
251
|
+
style={{ background: "url('data:image/webp;base64,…') center / contain no-repeat" }}
|
|
252
|
+
/>
|
|
253
|
+
}
|
|
254
|
+
>
|
|
255
|
+
<MuxVideo source={{ playbackId: 'EcHgOK9coz5K…' }} />
|
|
256
|
+
</VideoSkin>
|
|
257
|
+
</VideoPlayer>
|
|
252
258
|
```
|
|
253
259
|
|
|
254
|
-
`
|
|
260
|
+
`renderPoster` customizes the poster image, so its background can show Mux Player’s `placeholder` while the real poster loads.
|
|
255
261
|
|
|
256
|
-
Mux serves the poster from its `thumbnail` endpoint, so every Mux Player poster attribute becomes a query parameter you append: `thumbnail-time` is `?time=`, and the size, crop, rotation, and format options work the same way. See the [Poster](../reference/poster.md) reference for the component itself.
|
|
262
|
+
Mux serves the poster from its `thumbnail` endpoint, so every Mux Player poster attribute becomes a query parameter you append: `thumbnail-time` is `?time=`, and the size, crop, rotation, and format options work the same way. See [Add a poster placeholder](./add-a-poster-placeholder.md) for more examples or the [Poster](../reference/poster.md) reference for the component itself.
|
|
257
263
|
|
|
258
264
|
## Customize your player
|
|
259
265
|
|
package/docs/llms.txt
CHANGED
|
@@ -40,6 +40,7 @@ Understanding-oriented pages that explain how and why things work. Read these to
|
|
|
40
40
|
Task-oriented guides with step-by-step instructions to achieve a specific outcome by applying one or more concepts. Each guide may assume you already understand the relevant concepts.
|
|
41
41
|
|
|
42
42
|
- [Customize skins](./how-to/customize-skins.md): Learn how to customize Video.js v10 skins by copying and modifying them
|
|
43
|
+
- [Add a poster placeholder](./how-to/add-a-poster-placeholder.md): Show a lightweight placeholder while the poster image loads
|
|
43
44
|
- [Build your own UI component](./how-to/build-your-own-component.md): Create custom player controls that read state, dispatch actions, and stay accessible.
|
|
44
45
|
### Internationalization
|
|
45
46
|
|
|
@@ -84,6 +85,7 @@ API Reference for UI components for building media player interfaces.
|
|
|
84
85
|
- [Thumbnail](./reference/thumbnail.md): Time-based thumbnail preview component for timeline scrubbing and hover previews
|
|
85
86
|
- [Time](./reference/time.md): Time display components for showing current time, duration, and remaining time in a video player
|
|
86
87
|
- [TimeSlider](./reference/time-slider.md): A slider component for seeking through media playback time
|
|
88
|
+
- [Title](./reference/title.md): Displays the resolved content title for the current media
|
|
87
89
|
- [Tooltip](./reference/tooltip.md): A tooltip component for displaying contextual labels on hover and focus
|
|
88
90
|
- [VolumeSlider](./reference/volume-slider.md): A slider component for controlling media playback volume
|
|
89
91
|
|
|
@@ -110,6 +112,7 @@ API reference for the feature modules passed to createPlayer, which provide play
|
|
|
110
112
|
- [Error](./reference/feature-error.md): Media error state and actions for the player store
|
|
111
113
|
- [Fullscreen](./reference/feature-fullscreen.md): Fullscreen state and actions for the player store
|
|
112
114
|
- [Live](./reference/feature-live.md): Live edge state for the player store
|
|
115
|
+
- [Metadata](./reference/feature-metadata.md): Resolved title and poster values for the player store
|
|
113
116
|
- [Orientation lock](./reference/feature-orientation-lock.md): Screen orientation locking while fullscreen is active
|
|
114
117
|
- [Picture-in-picture](./reference/feature-pip.md): Picture-in-picture state and actions for the player store
|
|
115
118
|
- [Playback](./reference/feature-playback.md): Play/pause state and actions for the player store
|
|
@@ -97,15 +97,16 @@ function FullscreenButton() {
|
|
|
97
97
|
|
|
98
98
|
Add [`features.orientationLock`](./feature-orientation-lock.md) to lock screen orientation while fullscreen is active. Unsupported browsers and rejected lock requests are ignored, so iOS Safari continues to use its normal fullscreen behavior.
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
The feature locks to `landscape` unless the provider sets another Screen Orientation API type.
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
tsx
|
|
103
103
|
|
|
104
104
|
```
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
<Player orientationLockType="portrait">
|
|
106
|
+
<Container>
|
|
107
|
+
<video src="/video.mp4" />
|
|
108
|
+
</Container>
|
|
109
|
+
</Player>
|
|
110
|
+
```
|
|
107
111
|
|
|
108
|
-
|
|
109
|
-
features: [...videoFeatures, features.orientationLock({ type: 'portrait' })],
|
|
110
|
-
});
|
|
111
|
-
```
|
|
112
|
+
See [Orientation lock](./feature-orientation-lock.md) for the player setup.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Metadata
|
|
2
|
+
|
|
3
|
+
Resolved title and poster values for the player store
|
|
4
|
+
|
|
5
|
+
Resolves what is playing into two values your UI can render: `title` and `poster`.
|
|
6
|
+
|
|
7
|
+
`title` and `poster` each resolve independently through the same two tiers: the value you set, then the value the media reports. The first tier that holds a value wins; when neither does, the resolved value is an empty string. Because the tiers stay separate, clearing the value you set reveals the media’s, and a source that reports a poster but no title contributes to one while leaving the other empty.
|
|
8
|
+
|
|
9
|
+
An empty string counts as a value and stops the chain: set `title` to `''` and the resolved title is `''`. Pass `null` to clear your value.
|
|
10
|
+
|
|
11
|
+
The metadata feature is included by the `videoFeatures`, `audioFeatures`, `liveVideoFeatures`, and `liveAudioFeatures` [presets](../concepts/presets.md); apps that build a custom preset can compose it in directly.
|
|
12
|
+
|
|
13
|
+
## API Reference
|
|
14
|
+
|
|
15
|
+
### Configuration
|
|
16
|
+
|
|
17
|
+
Props the Player component accepts. They exist only while this feature is selected.
|
|
18
|
+
|
|
19
|
+
Prop
|
|
20
|
+
|
|
21
|
+
Type
|
|
22
|
+
|
|
23
|
+
Default
|
|
24
|
+
|
|
25
|
+
Details
|
|
26
|
+
|
|
27
|
+
`title`
|
|
28
|
+
|
|
29
|
+
`undefined | null | string`
|
|
30
|
+
|
|
31
|
+
`—`
|
|
32
|
+
|
|
33
|
+
▸
|
|
34
|
+
|
|
35
|
+
Description
|
|
36
|
+
|
|
37
|
+
The title to display. Takes precedence over the title the media carries.
|
|
38
|
+
|
|
39
|
+
`poster`
|
|
40
|
+
|
|
41
|
+
`undefined | null | string`
|
|
42
|
+
|
|
43
|
+
`—`
|
|
44
|
+
|
|
45
|
+
▸
|
|
46
|
+
|
|
47
|
+
Description
|
|
48
|
+
|
|
49
|
+
The poster to display. Takes precedence over the poster the media carries.
|
|
50
|
+
|
|
51
|
+
### State
|
|
52
|
+
|
|
53
|
+
Property
|
|
54
|
+
|
|
55
|
+
Type
|
|
56
|
+
|
|
57
|
+
Details
|
|
58
|
+
|
|
59
|
+
`title`
|
|
60
|
+
|
|
61
|
+
`string`
|
|
62
|
+
|
|
63
|
+
▸
|
|
64
|
+
|
|
65
|
+
Description
|
|
66
|
+
|
|
67
|
+
The resolved content title. Set it through the player, not through the store.
|
|
68
|
+
|
|
69
|
+
`poster`
|
|
70
|
+
|
|
71
|
+
`string`
|
|
72
|
+
|
|
73
|
+
▸
|
|
74
|
+
|
|
75
|
+
Description
|
|
76
|
+
|
|
77
|
+
The resolved poster URL, independent of the media element's own `poster`. Set it through the player, not through the store.
|
|
78
|
+
|
|
79
|
+
### Selector
|
|
80
|
+
|
|
81
|
+
Pass `selectMetadata` to [`usePlayer`](./use-player.md) to subscribe to metadata state. Returns `undefined` if the metadata feature is not configured.
|
|
82
|
+
|
|
83
|
+
ContentTitle.tsx
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
import { selectMetadata, usePlayer } from '@videojs/react';
|
|
87
|
+
|
|
88
|
+
function ContentTitle() {
|
|
89
|
+
const metadata = usePlayer(selectMetadata);
|
|
90
|
+
if (!metadata?.title) return null;
|
|
91
|
+
|
|
92
|
+
return <h2 className="content-title">{metadata.title}</h2>;
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Player inputs
|
|
97
|
+
|
|
98
|
+
The store publishes the resolved values and takes no writes. A player input is the only way to set one.
|
|
99
|
+
|
|
100
|
+
Every input is a prop on [`Player`](./player-provider.md). A player built without the metadata feature has none of them, and none is forwarded.
|
|
101
|
+
|
|
102
|
+
App.tsx
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
import { Container, createPlayer } from '@videojs/react';
|
|
106
|
+
import { videoFeatures } from '@videojs/react/video';
|
|
107
|
+
|
|
108
|
+
const { Player } = createPlayer({ features: videoFeatures });
|
|
109
|
+
|
|
110
|
+
function App({ episode }: { episode: { title: string | null; art: string | null } }) {
|
|
111
|
+
return (
|
|
112
|
+
<Player title={episode.title} poster={episode.art}>
|
|
113
|
+
<Container>
|
|
114
|
+
<video src="episode.mp4" />
|
|
115
|
+
</Container>
|
|
116
|
+
</Player>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Passing `null` clears your value, so `title={null}` falls through to the title the media reports.
|
|
122
|
+
|
|
123
|
+
### Media-reported values
|
|
124
|
+
|
|
125
|
+
The media tier comes from media that reports content data and announces changes to it, and it covers `title` and `poster`. A media reports only the keys it can vouch for, so either may be absent while the other arrives. A key that never arrives means that tier never contributes, and the resolved value is whatever you set. Detaching the media clears its values; the values you set survive and apply to the next source.
|
|
126
|
+
|
|
127
|
+
`poster` is the feature’s own resolved value, not the media element’s `poster` attribute. Setting one does not set the other.
|
|
128
|
+
|
|
129
|
+
A low-resolution stand-in to show while the poster loads is not part of this feature. The [poster](./poster.md) renders an `<img>` you control, so give it a `background-image` and that shows until the poster itself paints over it.
|
|
@@ -6,14 +6,74 @@ Locks screen orientation while fullscreen is active. Add it explicitly to a feat
|
|
|
6
6
|
|
|
7
7
|
## API Reference
|
|
8
8
|
|
|
9
|
+
### Configuration
|
|
10
|
+
|
|
11
|
+
Props the Player component accepts. They exist only while this feature is selected.
|
|
12
|
+
|
|
13
|
+
Prop
|
|
14
|
+
|
|
15
|
+
Type
|
|
16
|
+
|
|
17
|
+
Default
|
|
18
|
+
|
|
19
|
+
Details
|
|
20
|
+
|
|
21
|
+
`orientationLockType`
|
|
22
|
+
|
|
23
|
+
`undefined | null | 'any' | 'landscape' | 'landscape-primary' | 'landscape-secondary' | 'natural' | 'portrait' | 'portrait-primary' | 'portrait-secondary'`
|
|
24
|
+
|
|
25
|
+
`'landscape'`
|
|
26
|
+
|
|
27
|
+
▸
|
|
28
|
+
|
|
29
|
+
Description
|
|
30
|
+
|
|
31
|
+
Screen orientation type to lock while fullscreen is active.
|
|
32
|
+
|
|
33
|
+
### State
|
|
34
|
+
|
|
35
|
+
Property
|
|
36
|
+
|
|
37
|
+
Type
|
|
38
|
+
|
|
39
|
+
Details
|
|
40
|
+
|
|
41
|
+
`orientationLockType`
|
|
42
|
+
|
|
43
|
+
`'any' | 'landscape' | 'landscape-primary' | 'landscape-secondary' | 'natural' | 'portrait' | 'portrait-primary' | 'portrait-secondary'`
|
|
44
|
+
|
|
45
|
+
▸
|
|
46
|
+
|
|
47
|
+
Description
|
|
48
|
+
|
|
49
|
+
Screen orientation type locked while fullscreen is active.
|
|
50
|
+
|
|
51
|
+
### Actions
|
|
52
|
+
|
|
53
|
+
Action
|
|
54
|
+
|
|
55
|
+
Type
|
|
56
|
+
|
|
57
|
+
Details
|
|
58
|
+
|
|
59
|
+
`setOrientationLockType`
|
|
60
|
+
|
|
61
|
+
`(value: undefined | null | 'any' | 'landscape' | 'landscape-primary' | 'landscape-secondary' | 'natural' | 'portrait' | 'portrait-primary' | 'portrait-secondary') => void`
|
|
62
|
+
|
|
63
|
+
▸
|
|
64
|
+
|
|
65
|
+
Description
|
|
66
|
+
|
|
67
|
+
Sets the locked orientation type. Absent input — nullish, or the empty string a valueless HTML attribute produces — restores the default.
|
|
68
|
+
|
|
9
69
|
### Usage
|
|
10
70
|
|
|
11
|
-
|
|
71
|
+
Selecting the feature adds it to the player.
|
|
12
72
|
|
|
13
73
|
player.tsx
|
|
14
74
|
|
|
15
75
|
```
|
|
16
|
-
import { createPlayer, features } from '@videojs/react';
|
|
76
|
+
import { Container, createPlayer, features } from '@videojs/react';
|
|
17
77
|
import { videoFeatures } from '@videojs/react/video';
|
|
18
78
|
|
|
19
79
|
export const { Player } = createPlayer({
|
|
@@ -21,15 +81,41 @@ export const { Player } = createPlayer({
|
|
|
21
81
|
});
|
|
22
82
|
```
|
|
23
83
|
|
|
24
|
-
|
|
84
|
+
### Orientation type
|
|
85
|
+
|
|
86
|
+
The feature locks to `landscape` unless the provider sets another Screen Orientation API type. The value can change while the player is running; if the screen is already locked, it re-locks to the new type.
|
|
87
|
+
|
|
88
|
+
tsx
|
|
25
89
|
|
|
26
90
|
```
|
|
27
|
-
|
|
28
|
-
|
|
91
|
+
<Player orientationLockType="portrait">
|
|
92
|
+
<Container>
|
|
93
|
+
<video src="/video.mp4" />
|
|
94
|
+
</Container>
|
|
95
|
+
</Player>
|
|
96
|
+
```
|
|
29
97
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
98
|
+
Clearing the value restores `landscape`.
|
|
99
|
+
|
|
100
|
+
### Selector
|
|
101
|
+
|
|
102
|
+
Pass `selectOrientationLock` to [`usePlayer`](./use-player.md) to subscribe to the lock state. Returns `undefined` if the orientation lock feature is not configured.
|
|
103
|
+
|
|
104
|
+
OrientationToggle.tsx
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
import { selectOrientationLock, usePlayer } from '@videojs/react';
|
|
108
|
+
|
|
109
|
+
function OrientationToggle() {
|
|
110
|
+
const lock = usePlayer(selectOrientationLock);
|
|
111
|
+
if (!lock) return null;
|
|
112
|
+
|
|
113
|
+
return (
|
|
114
|
+
<button onClick={() => lock.setOrientationLockType('portrait')}>
|
|
115
|
+
Lock portrait
|
|
116
|
+
</button>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
33
119
|
```
|
|
34
120
|
|
|
35
121
|
Unsupported browsers and rejected lock requests are ignored.
|
|
@@ -46,6 +46,16 @@ Description
|
|
|
46
46
|
|
|
47
47
|
The `<track>` element's `src` for resolving relative cue text URLs.
|
|
48
48
|
|
|
49
|
+
`thumbnailTrackCrossOrigin`
|
|
50
|
+
|
|
51
|
+
`null | 'anonymous' | 'use-credentials'`
|
|
52
|
+
|
|
53
|
+
▸
|
|
54
|
+
|
|
55
|
+
Description
|
|
56
|
+
|
|
57
|
+
The media element's CORS mode, mapped through the CORS-settings-attribute rules, or `null` when it is not in CORS mode. Thumbnail UI fetches the sprite sheets the cues point at with this mode, since a cross-origin `<track>` only loads at all when the media element is CORS-enabled.
|
|
58
|
+
|
|
49
59
|
`textTrackList`
|
|
50
60
|
|
|
51
61
|
`MediaTextTrack<TextTrackKind>[]`
|
package/docs/reference/menu.md
CHANGED
|
@@ -442,7 +442,6 @@ export default function BasicUsage() {
|
|
|
442
442
|
transition-timing-function: ease-in-out;
|
|
443
443
|
transition-duration: var(--menu-transition-duration);
|
|
444
444
|
transition-property: translate, filter;
|
|
445
|
-
will-change: translate;
|
|
446
445
|
}
|
|
447
446
|
|
|
448
447
|
.menu[data-submenu-expanded="true"] > :not([data-submenu]) {
|
|
@@ -227,6 +227,10 @@ Description
|
|
|
227
227
|
|
|
228
228
|
CORS setting forwarded to the inner `<img>`.
|
|
229
229
|
|
|
230
|
+
Left unset, this follows the media element: a cross-origin thumbnail `<track>` only loads when the media is CORS-enabled, so the sprite sheets its cues point at are fetched with that same mode. Pass `null` to opt out and fetch them without CORS. Thumbnails supplied directly never inherit, since they need not be related to the media element at all.
|
|
231
|
+
|
|
232
|
+
`''` is a value like any other, read as Anonymous by the CORS-settings attribute rules — it does not opt out.
|
|
233
|
+
|
|
230
234
|
Type
|
|
231
235
|
|
|
232
236
|
`'anonymous' | 'use-credentials' | '' | null`
|