@mattebox/player 0.2.0 → 0.3.1

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 (51) hide show
  1. package/README.md +4 -1
  2. package/dist/cdn/mattebox-player.min.js +49 -29
  3. package/dist/controls/slider.d.ts.map +1 -1
  4. package/dist/controls/slider.js +16 -2
  5. package/dist/controls/slider.js.map +1 -1
  6. package/dist/elements/component.d.ts +2 -2
  7. package/dist/elements/component.d.ts.map +1 -1
  8. package/dist/elements/component.js +3 -3
  9. package/dist/elements/component.js.map +1 -1
  10. package/dist/elements/control-bar.d.ts +29 -0
  11. package/dist/elements/control-bar.d.ts.map +1 -1
  12. package/dist/elements/control-bar.js +136 -4
  13. package/dist/elements/control-bar.js.map +1 -1
  14. package/dist/elements/remaining-time.d.ts +6 -0
  15. package/dist/elements/remaining-time.d.ts.map +1 -0
  16. package/dist/elements/remaining-time.js +26 -0
  17. package/dist/elements/remaining-time.js.map +1 -0
  18. package/dist/elements/start-button.d.ts +10 -0
  19. package/dist/elements/start-button.d.ts.map +1 -1
  20. package/dist/elements/start-button.js +37 -0
  21. package/dist/elements/start-button.js.map +1 -1
  22. package/dist/elements/volume.d.ts.map +1 -1
  23. package/dist/elements/volume.js +2 -0
  24. package/dist/elements/volume.js.map +1 -1
  25. package/dist/entries/remaining-time.d.ts +5 -0
  26. package/dist/entries/remaining-time.d.ts.map +1 -0
  27. package/dist/entries/remaining-time.js +9 -0
  28. package/dist/entries/remaining-time.js.map +1 -0
  29. package/dist/es2015/controls/slider.js +13 -1
  30. package/dist/es2015/elements/component.js +4 -3
  31. package/dist/es2015/elements/control-bar.js +126 -5
  32. package/dist/es2015/elements/remaining-time.js +26 -0
  33. package/dist/es2015/elements/start-button.js +37 -0
  34. package/dist/es2015/elements/volume.js +2 -0
  35. package/dist/es2015/entries/remaining-time.js +7 -0
  36. package/dist/es2015/index.js +3 -1
  37. package/dist/es2015/style.js +36 -15
  38. package/dist/es2015/tags.js +2 -1
  39. package/dist/index.d.ts +3 -0
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +1 -0
  42. package/dist/index.js.map +1 -1
  43. package/dist/style.d.ts +7 -1
  44. package/dist/style.d.ts.map +1 -1
  45. package/dist/style.js +36 -15
  46. package/dist/style.js.map +1 -1
  47. package/dist/tags.d.ts +1 -0
  48. package/dist/tags.d.ts.map +1 -1
  49. package/dist/tags.js +1 -0
  50. package/dist/tags.js.map +1 -1
  51. package/package.json +3 -3
@@ -17,6 +17,11 @@ import { el } from "../dom.js";
17
17
  *
18
18
  * The steps are read on every key, so an element can take them from its
19
19
  * attributes as they change.
20
+ *
21
+ * A drag ends on `pointerup`, and also on a move that reports no button
22
+ * held: the button went up where no `pointerup` reached the slider, such
23
+ * as outside the window, and the thumb must not stay on the pointer. A
24
+ * lost capture ends it too.
20
25
  */
21
26
  function clamp(value, min, max) {
22
27
  return Math.min(max, Math.max(min, value));
@@ -62,7 +67,12 @@ function slider(options) {
62
67
  onInput(at(event.clientX));
63
68
  }
64
69
  function move(event) {
65
- if (held) onInput(at(event.clientX));
70
+ if (!held) return;
71
+ if (event.buttons === 0) {
72
+ up(event);
73
+ return;
74
+ }
75
+ onInput(at(event.clientX));
66
76
  }
67
77
  function up(event) {
68
78
  if (!held) return;
@@ -104,6 +114,7 @@ function slider(options) {
104
114
  root.addEventListener("pointermove", move);
105
115
  root.addEventListener("pointerup", up);
106
116
  root.addEventListener("pointercancel", cancel);
117
+ root.addEventListener("lostpointercapture", cancel);
107
118
  root.addEventListener("keydown", key);
108
119
  return {
109
120
  root,
@@ -133,6 +144,7 @@ function slider(options) {
133
144
  root.removeEventListener("pointermove", move);
134
145
  root.removeEventListener("pointerup", up);
135
146
  root.removeEventListener("pointercancel", cancel);
147
+ root.removeEventListener("lostpointercapture", cancel);
136
148
  root.removeEventListener("keydown", key);
137
149
  }
138
150
  };
@@ -64,12 +64,13 @@ var Component = class extends HTMLElement {
64
64
  });
65
65
  run();
66
66
  }
67
- /** Runs `fn` when any of `names` changes on `target`, until detach. */
68
- observe(target, names, fn) {
67
+ /** Runs `fn` when any of `names` changes on `target`, or under it with `subtree`, until detach. */
68
+ observe(target, names, fn, subtree = false) {
69
69
  const observer = new MutationObserver(fn);
70
70
  observer.observe(target, {
71
71
  attributes: true,
72
- attributeFilter: [...names]
72
+ attributeFilter: [...names],
73
+ subtree
73
74
  });
74
75
  this.offs.push(() => {
75
76
  observer.disconnect();
@@ -1,3 +1,4 @@
1
+ import { AUDIO_MENU, CHAPTERS_MENU, DRM_BADGE, MUTE_BUTTON, PIP_BUTTON, QUALITY_MENU, SKIP_BUTTON, SPEED_MENU, SUBTITLES_MENU, VOLUME, VOLUME_SLIDER } from "../tags.js";
1
2
  import { Component } from "./component.js";
2
3
  import { number, style } from "./shared.js";
3
4
  import { cueLift, cueStyle } from "../controls/cues.js";
@@ -5,6 +6,50 @@ import { fullscreen } from "../controls/fullscreen.js";
5
6
  //#region src/elements/control-bar.ts
6
7
  const IDLE_MS = 3e3;
7
8
  const SEEK_STEP = 5;
9
+ /**
10
+ * What goes first when the row is too narrow, by tag: the slider, which
11
+ * takes the most room for the least; then the diagnostics, the lock and
12
+ * the menus a viewer rarely opens; then the menus for the picture and the
13
+ * sound; then the skips; then the mute and the subtitles. The subtitles
14
+ * menu goes last of the menus because it is the one a viewer may need.
15
+ * The diagnostics is another package's, named here as a string. A
16
+ * `priority` attribute on the control replaces its default.
17
+ */
18
+ const PRIORITY = {
19
+ [VOLUME_SLIDER]: 5,
20
+ "mbx-diagnostics": 4,
21
+ [DRM_BADGE]: 4,
22
+ [SPEED_MENU]: 4,
23
+ [CHAPTERS_MENU]: 4,
24
+ [AUDIO_MENU]: 3,
25
+ [QUALITY_MENU]: 3,
26
+ [PIP_BUTTON]: 3,
27
+ [SKIP_BUTTON]: 2,
28
+ [SUBTITLES_MENU]: 1,
29
+ [VOLUME]: 1,
30
+ [MUTE_BUTTON]: 1
31
+ };
32
+ /** The width the volume group unfolds its slider to, from volume.ts: what a folded slider is counted at. */
33
+ const SLIDER_WIDTH = 80;
34
+ /** Room kept between the last button and the edge, so nothing sits flush against it. */
35
+ const SLACK = 8;
36
+ function shown(node) {
37
+ return node.getClientRects().length > 0;
38
+ }
39
+ /** The slider inside a volume group child, when it has one. */
40
+ function slider(child) {
41
+ return child.localName === "mbx-volume" ? child.querySelector(VOLUME_SLIDER) : null;
42
+ }
43
+ /** A child's priority: its attribute, else its tag's default, else zero. */
44
+ function priority(child) {
45
+ var _PRIORITY$child$local;
46
+ const own = child.getAttribute("priority");
47
+ if (own !== null) {
48
+ const value = Number(own);
49
+ return Number.isFinite(value) && value > 0 ? value : 0;
50
+ }
51
+ return (_PRIORITY$child$local = PRIORITY[child.localName]) !== null && _PRIORITY$child$local !== void 0 ? _PRIORITY$child$local : 0;
52
+ }
8
53
  const STYLE = `
9
54
  :host {
10
55
  position: absolute;
@@ -22,6 +67,7 @@ const STYLE = `
22
67
  :host([idle]) { opacity: 0; pointer-events: none; }
23
68
  [part~="row"] { display: flex; align-items: center; gap: 8px; }
24
69
  [part~="seek-row"] { gap: 16px; }
70
+ ::slotted([collapsed]) { display: none; }
25
71
  @media (prefers-reduced-motion: reduce) {
26
72
  :host { transition: none; }
27
73
  }
@@ -45,16 +91,70 @@ var MbxControlBar = class extends Component {
45
91
  this.keyboard = false;
46
92
  this.cues = null;
47
93
  this.screen = null;
94
+ this.resizer = null;
48
95
  const root = this.attachShadow({ mode: "open" });
49
96
  this.seekRow = document.createElement("div");
50
97
  this.seekRow.setAttribute("part", "row seek-row");
51
98
  const seek = document.createElement("slot");
52
99
  seek.name = "seek";
53
100
  this.seekRow.append(seek);
54
- const buttons = document.createElement("div");
55
- buttons.setAttribute("part", "row buttons-row");
56
- buttons.append(document.createElement("slot"));
57
- root.append(style(STYLE), this.seekRow, buttons);
101
+ this.buttonsRow = document.createElement("div");
102
+ this.buttonsRow.setAttribute("part", "row buttons-row");
103
+ this.buttons = document.createElement("slot");
104
+ this.buttonsRow.append(this.buttons);
105
+ root.append(style(STYLE), this.seekRow, this.buttonsRow);
106
+ }
107
+ /**
108
+ * What the buttons need: their widths and the gaps between them, the
109
+ * spacer aside since it takes what is left, and a folded slider at the
110
+ * width it unfolds to. The row's own width says nothing, because the
111
+ * spacer fills it whatever the buttons take.
112
+ */
113
+ needed(children) {
114
+ const gap = Number.parseFloat(getComputedStyle(this.buttonsRow).columnGap) || 0;
115
+ let sum = 0;
116
+ let count = 0;
117
+ for (const child of children) {
118
+ if (child.localName === "mbx-spacer" || !shown(child)) continue;
119
+ sum += child.getBoundingClientRect().width;
120
+ count += 1;
121
+ const folded = slider(child);
122
+ if (folded !== null && shown(folded)) sum += Math.max(0, SLIDER_WIDTH - folded.getBoundingClientRect().width);
123
+ }
124
+ return sum + gap * Math.max(0, count - 1);
125
+ }
126
+ /**
127
+ * Collapses the buttons row to its width. Everything is shown first, so
128
+ * a wider bar gets its controls back, then the highest priorities go one
129
+ * at a time while the buttons need more than the row has, less the
130
+ * slack. Each step lays the row out again; a bar carries a dozen
131
+ * controls, so that is cheap.
132
+ */
133
+ collapse() {
134
+ const children = this.buttons.assignedElements();
135
+ const candidates = [];
136
+ for (const [index, child] of children.entries()) {
137
+ child.removeAttribute("collapsed");
138
+ const inner = slider(child);
139
+ inner === null || inner === void 0 || inner.removeAttribute("collapsed");
140
+ if (inner !== null && shown(inner)) candidates.push({
141
+ node: inner,
142
+ index: index + .5,
143
+ priority: priority(inner)
144
+ });
145
+ if (shown(child)) candidates.push({
146
+ node: child,
147
+ index,
148
+ priority: priority(child)
149
+ });
150
+ }
151
+ candidates.sort((a, b) => b.priority - a.priority || b.index - a.index);
152
+ const room = this.buttonsRow.clientWidth - SLACK;
153
+ for (const { node, priority: level } of candidates) {
154
+ if (level === 0) return;
155
+ if (this.needed(children) <= room) return;
156
+ node.setAttribute("collapsed", "");
157
+ }
58
158
  }
59
159
  /** From the first row's top to the bar's bottom: what the bar covers of the picture. */
60
160
  covered() {
@@ -161,11 +261,32 @@ var MbxControlBar = class extends Component {
161
261
  });
162
262
  this.wake(player);
163
263
  this.cues.lifted(true);
264
+ const collapse = () => {
265
+ this.collapse();
266
+ };
267
+ this.listen(this.buttons, ["slotchange"], collapse);
268
+ this.observe(this, [
269
+ "hidden",
270
+ "priority",
271
+ "slot"
272
+ ], collapse, true);
273
+ if (typeof ResizeObserver !== "undefined") {
274
+ this.resizer = new ResizeObserver(collapse);
275
+ this.resizer.observe(this.buttonsRow);
276
+ }
277
+ collapse();
164
278
  }
165
279
  detach(player) {
166
- var _this$cues2;
280
+ var _this$resizer, _this$cues2;
167
281
  clearTimeout(this.timer);
168
282
  this.timer = void 0;
283
+ (_this$resizer = this.resizer) === null || _this$resizer === void 0 || _this$resizer.disconnect();
284
+ this.resizer = null;
285
+ for (const child of this.buttons.assignedElements()) {
286
+ var _slider;
287
+ child.removeAttribute("collapsed");
288
+ (_slider = slider(child)) === null || _slider === void 0 || _slider.removeAttribute("collapsed");
289
+ }
169
290
  (_this$cues2 = this.cues) === null || _this$cues2 === void 0 || _this$cues2.dispose();
170
291
  this.cues = null;
171
292
  this.screen = null;
@@ -0,0 +1,26 @@
1
+ import { format } from "../controls/time.js";
2
+ import { live, span } from "../controls/session.js";
3
+ import { TimeElement } from "./time-element.js";
4
+ //#region src/elements/remaining-time.ts
5
+ /**
6
+ * <mbx-remaining-time>: what is left, "-4:53", once the metadata is in. A
7
+ * bar that shows one number shows this one: a viewer asks how much is
8
+ * left more often than where they are. A live stream has no end, so the
9
+ * element hides while the stream is live, as the duration does.
10
+ */
11
+ var MbxRemainingTime = class extends TimeElement {
12
+ constructor() {
13
+ super(":host { text-align: right; }");
14
+ }
15
+ render() {
16
+ const player = this.player;
17
+ if (player === null) return;
18
+ const video = player.video;
19
+ const engine = player.engine;
20
+ const on = live(engine) !== void 0;
21
+ this.hidden = on;
22
+ if (!on) this.text.data = `-${format(Math.max(0, span(video, engine).end - video.currentTime))}`;
23
+ }
24
+ };
25
+ //#endregion
26
+ export { MbxRemainingTime };
@@ -20,6 +20,8 @@ const STYLE = `${BUTTON_STYLE}
20
20
  transition: opacity 0.15s, background 0.15s;
21
21
  }
22
22
  [part~="button"]:hover { opacity: 1; background: rgba(0, 0, 0, 0.6); }
23
+ /* A box too short for the button above the bar: the bar's play does the job. */
24
+ :host([cramped]) { display: none; }
23
25
  [part~="icon"], ::slotted(*) { width: 40px; height: 40px; margin-left: 2px; }
24
26
  @media (prefers-reduced-motion: reduce) {
25
27
  [part~="button"] { transition: none; }
@@ -32,6 +34,7 @@ var MbxStartButton = class extends Component {
32
34
  constructor() {
33
35
  super();
34
36
  this.failed = false;
37
+ this.resizer = null;
35
38
  const root = this.attachShadow({ mode: "open" });
36
39
  this.button = document.createElement("button");
37
40
  this.button.type = "button";
@@ -71,8 +74,42 @@ var MbxStartButton = class extends Component {
71
74
  player.removeEventListener("error", failure);
72
75
  });
73
76
  this.failed = player.error !== null;
77
+ if (typeof ResizeObserver !== "undefined") {
78
+ this.resizer = new ResizeObserver(() => {
79
+ this.fit(player);
80
+ });
81
+ this.resizer.observe(player);
82
+ }
83
+ this.fit(player);
74
84
  this.render();
75
85
  }
86
+ detach() {
87
+ var _this$resizer;
88
+ (_this$resizer = this.resizer) === null || _this$resizer === void 0 || _this$resizer.disconnect();
89
+ this.resizer = null;
90
+ this.removeAttribute("cramped");
91
+ }
92
+ /**
93
+ * Cramped when the button's foot would be inside the bar's box, which
94
+ * starts at the top of its gradient: then the bar's own play button is
95
+ * the one to press. Measured with the button shown, since a hidden one
96
+ * has no box.
97
+ */
98
+ fit(player) {
99
+ const bar = player.querySelector("mbx-control-bar");
100
+ if (bar === null) {
101
+ this.removeAttribute("cramped");
102
+ return;
103
+ }
104
+ const wasHidden = this.hidden;
105
+ const wasCramped = this.hasAttribute("cramped");
106
+ this.hidden = false;
107
+ this.removeAttribute("cramped");
108
+ const cramped = this.button.getBoundingClientRect().bottom > bar.getBoundingClientRect().top;
109
+ this.hidden = wasHidden;
110
+ this.toggleAttribute("cramped", cramped);
111
+ if (cramped !== wasCramped) this.render();
112
+ }
76
113
  render() {
77
114
  var _this$player2, _this$getAttribute;
78
115
  const video = (_this$player2 = this.player) === null || _this$player2 === void 0 ? void 0 : _this$player2.video;
@@ -19,6 +19,8 @@ const STYLE = `
19
19
  :host { display: inline-flex; align-items: center; }
20
20
  :host([hidden]) { display: none; }
21
21
  ::slotted(mbx-volume-slider) { width: 0; opacity: 0; overflow: hidden; transition: width 0.2s ease, opacity 0.2s ease; }
22
+ /* The bar collapses the slider on its own when the row is narrow; then the pointer unfolds nothing. */
23
+ ::slotted(mbx-volume-slider[collapsed]) { display: none; }
22
24
  :host(:hover) ::slotted(mbx-volume-slider), :host(:focus-within:not([pointer])) ::slotted(mbx-volume-slider) { width: 80px; opacity: 1; }
23
25
  @media (prefers-reduced-motion: reduce) {
24
26
  ::slotted(mbx-volume-slider) { transition: none; }
@@ -0,0 +1,7 @@
1
+ import { REMAINING_TIME } from "../tags.js";
2
+ import "../element-entry.js";
3
+ import { MbxRemainingTime } from "../elements/remaining-time.js";
4
+ //#region src/entries/remaining-time.ts
5
+ if (customElements.get("mbx-remaining-time") === void 0) customElements.define(REMAINING_TIME, MbxRemainingTime);
6
+ //#endregion
7
+ export { MbxRemainingTime };
@@ -28,6 +28,8 @@ import { MbxPlayButton } from "./elements/play-button.js";
28
28
  import "./entries/play-button.js";
29
29
  import { MbxQualityMenu } from "./elements/quality-menu.js";
30
30
  import "./entries/quality-menu.js";
31
+ import { MbxRemainingTime } from "./elements/remaining-time.js";
32
+ import "./entries/remaining-time.js";
31
33
  import { MbxSeekBar } from "./elements/seek-bar.js";
32
34
  import "./entries/seek-bar.js";
33
35
  import { MbxSkipButton } from "./elements/skip-button.js";
@@ -44,4 +46,4 @@ import { MbxVolumeSlider } from "./elements/volume-slider.js";
44
46
  import "./entries/volume-slider.js";
45
47
  import { MbxVolume } from "./elements/volume.js";
46
48
  import "./entries/volume.js";
47
- export { MatteboxPlayerElement, MbxAudioMenu, MbxChaptersMenu, MbxControlBar, MbxCurrentTime, MbxDrmBadge, MbxDuration, MbxErrorScreen, MbxFullscreenButton, MbxLiveButton, MbxMuteButton, MbxPanels, MbxPipButton, MbxPlayButton, MbxQualityMenu, MbxSeekBar, MbxSkipButton, MbxSpacer, MbxSpeedMenu, MbxStartButton, MbxSubtitlesMenu, MbxVolume, MbxVolumeSlider };
49
+ export { MatteboxPlayerElement, MbxAudioMenu, MbxChaptersMenu, MbxControlBar, MbxCurrentTime, MbxDrmBadge, MbxDuration, MbxErrorScreen, MbxFullscreenButton, MbxLiveButton, MbxMuteButton, MbxPanels, MbxPipButton, MbxPlayButton, MbxQualityMenu, MbxRemainingTime, MbxSeekBar, MbxSkipButton, MbxSpacer, MbxSpeedMenu, MbxStartButton, MbxSubtitlesMenu, MbxVolume, MbxVolumeSlider };
@@ -4,6 +4,12 @@
4
4
  * error surface under native controls. Every control carries its own sheet.
5
5
  * Two rules govern them.
6
6
  *
7
+ * The box is black and the picture is centred in it, letterboxed or
8
+ * pillarboxed by `object-fit: contain`, whatever height or ratio the page
9
+ * gives the element: what fullscreen always did, now the default. With no
10
+ * height from the page the stage is as tall as the picture and none of it
11
+ * shows.
12
+ *
7
13
  * Nothing carries `!important`. For normal declarations the outer tree wins
8
14
  * over the shadow tree, so any `::part()` rule the page writes beats these
9
15
  * whatever its specificity; an `!important` here would invert that and make
@@ -17,6 +23,7 @@ const STYLE = `
17
23
  display: block;
18
24
  position: relative;
19
25
  outline: none;
26
+ background: #000;
20
27
  --mbx-surface: #101114;
21
28
  --mbx-text: #f2f3f5;
22
29
  --mbx-muted: #9aa0a6;
@@ -28,26 +35,40 @@ const STYLE = `
28
35
  --mbx-pad: 8px;
29
36
  --mbx-font: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
30
37
  }
31
- [part~="stage"] { position: relative; }
38
+ /* The stage is a column as tall as the host, so the picture is centred
39
+ in whatever height the page gives, and the panels row under native
40
+ controls keeps its place below the picture. The height comes from the
41
+ page, or from the browser in fullscreen, and not from a display on the
42
+ host: a page's own "mattebox-player { display: block }" beats a :host()
43
+ rule, so nothing here may depend on one. */
44
+ [part~="stage"] {
45
+ position: relative;
46
+ display: flex;
47
+ flex-direction: column;
48
+ justify-content: center;
49
+ height: 100%;
50
+ }
32
51
  /* 16:9 until the media says otherwise: a bare video measures 300 by 150 and
33
52
  would jump to its size on metadata. Auto first, so the natural ratio wins
34
- once it is known. */
35
- ::slotted(video) { display: block; width: 100%; aspect-ratio: auto 16 / 9; }
53
+ once it is known. Capped at the stage, and contained, so a box shorter
54
+ than the picture letterboxes it and a narrower one pillarboxes it. */
55
+ ::slotted(video) {
56
+ display: block;
57
+ width: 100%;
58
+ min-height: 0;
59
+ max-height: 100%;
60
+ object-fit: contain;
61
+ aspect-ratio: auto 16 / 9;
62
+ }
36
63
  :host(:focus-visible) [part~="stage"] { outline: 2px solid var(--mbx-accent); outline-offset: -2px; }
37
64
  /* Fullscreen goes on the host, so every control the page placed inside
38
- comes along. The stage takes the height and the video sits in it. The
39
- height comes from the browser, which gives the fullscreen element the
40
- whole screen with !important, and not from a display on the host:
41
- a page's own "mattebox-player { display: block }" beats a :host()
42
- rule, so nothing here may depend on one. The error surface, which sits
43
- under the stage in the page, goes over its foot instead. */
44
- :host(:fullscreen) { background: #000; }
45
- :host(:fullscreen) [part~="stage"] { height: 100%; display: flex; }
46
- :host(:fullscreen) ::slotted(video) { height: 100%; object-fit: contain; }
65
+ comes along. The browser gives the host the whole screen with
66
+ !important; the picture takes what the panels row leaves. The error
67
+ surface, which sits under the stage in the page, goes over its foot
68
+ instead. */
69
+ :host(:fullscreen) ::slotted(video) { flex: 1; }
47
70
  :host(:fullscreen) [part~="error"] { position: absolute; left: 0; right: 0; bottom: 0; }
48
- :host(:-webkit-full-screen) { background: #000; }
49
- :host(:-webkit-full-screen) [part~="stage"] { height: 100%; display: flex; }
50
- :host(:-webkit-full-screen) ::slotted(video) { height: 100%; object-fit: contain; }
71
+ :host(:-webkit-full-screen) ::slotted(video) { flex: 1; }
51
72
  :host(:-webkit-full-screen) [part~="error"] { position: absolute; left: 0; right: 0; bottom: 0; }
52
73
  [part~="error"] {
53
74
  display: flex;
@@ -13,6 +13,7 @@ const START_BUTTON = "mbx-start-button";
13
13
  const ERROR_SCREEN = "mbx-error-screen";
14
14
  const CURRENT_TIME = "mbx-current-time";
15
15
  const DURATION = "mbx-duration";
16
+ const REMAINING_TIME = "mbx-remaining-time";
16
17
  const SEEK_BAR = "mbx-seek-bar";
17
18
  const LIVE_BUTTON = "mbx-live-button";
18
19
  const SPEED_MENU = "mbx-speed-menu";
@@ -24,4 +25,4 @@ const PANELS = "mbx-panels";
24
25
  const VOLUME = "mbx-volume";
25
26
  const CHAPTERS_MENU = "mbx-chapters-menu";
26
27
  //#endregion
27
- export { AUDIO_MENU, CHAPTERS_MENU, CONTROL_BAR, CURRENT_TIME, DRM_BADGE, DURATION, ERROR_SCREEN, FULLSCREEN_BUTTON, LIVE_BUTTON, MUTE_BUTTON, PANELS, PIP_BUTTON, PLAYER, PLAY_BUTTON, QUALITY_MENU, SEEK_BAR, SKIP_BUTTON, SPACER, SPEED_MENU, START_BUTTON, SUBTITLES_MENU, VOLUME, VOLUME_SLIDER };
28
+ export { AUDIO_MENU, CHAPTERS_MENU, CONTROL_BAR, CURRENT_TIME, DRM_BADGE, DURATION, ERROR_SCREEN, FULLSCREEN_BUTTON, LIVE_BUTTON, MUTE_BUTTON, PANELS, PIP_BUTTON, PLAYER, PLAY_BUTTON, QUALITY_MENU, REMAINING_TIME, SEEK_BAR, SKIP_BUTTON, SPACER, SPEED_MENU, START_BUTTON, SUBTITLES_MENU, VOLUME, VOLUME_SLIDER };
package/dist/index.d.ts CHANGED
@@ -19,6 +19,7 @@ import type { MbxPanels } from './entries/panels.js';
19
19
  import type { MbxPipButton } from './entries/pip-button.js';
20
20
  import type { MbxPlayButton } from './entries/play-button.js';
21
21
  import type { MbxQualityMenu } from './entries/quality-menu.js';
22
+ import type { MbxRemainingTime } from './entries/remaining-time.js';
22
23
  import type { MbxSeekBar } from './entries/seek-bar.js';
23
24
  import type { MbxSkipButton } from './entries/skip-button.js';
24
25
  import type { MbxSpacer } from './entries/spacer.js';
@@ -43,6 +44,7 @@ export { MbxPanels } from './entries/panels.js';
43
44
  export { MbxPipButton } from './entries/pip-button.js';
44
45
  export { MbxPlayButton } from './entries/play-button.js';
45
46
  export { MbxQualityMenu } from './entries/quality-menu.js';
47
+ export { MbxRemainingTime } from './entries/remaining-time.js';
46
48
  export { MbxSeekBar } from './entries/seek-bar.js';
47
49
  export { MbxSkipButton } from './entries/skip-button.js';
48
50
  export { MbxSpacer } from './entries/spacer.js';
@@ -68,6 +70,7 @@ declare global {
68
70
  'mbx-error-screen': MbxErrorScreen;
69
71
  'mbx-current-time': MbxCurrentTime;
70
72
  'mbx-duration': MbxDuration;
73
+ 'mbx-remaining-time': MbxRemainingTime;
71
74
  'mbx-seek-bar': MbxSeekBar;
72
75
  'mbx-live-button': MbxLiveButton;
73
76
  'mbx-speed-menu': MbxSpeedMenu;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAC1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAElE,YAAY,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,YAAY,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE5C,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,iBAAiB,EAAE,qBAAqB,CAAC;QACzC,iBAAiB,EAAE,aAAa,CAAC;QACjC,YAAY,EAAE,SAAS,CAAC;QACxB,iBAAiB,EAAE,aAAa,CAAC;QACjC,iBAAiB,EAAE,aAAa,CAAC;QACjC,mBAAmB,EAAE,eAAe,CAAC;QACrC,YAAY,EAAE,SAAS,CAAC;QACxB,iBAAiB,EAAE,aAAa,CAAC;QACjC,gBAAgB,EAAE,YAAY,CAAC;QAC/B,uBAAuB,EAAE,mBAAmB,CAAC;QAC7C,kBAAkB,EAAE,cAAc,CAAC;QACnC,kBAAkB,EAAE,cAAc,CAAC;QACnC,kBAAkB,EAAE,cAAc,CAAC;QACnC,cAAc,EAAE,WAAW,CAAC;QAC5B,cAAc,EAAE,UAAU,CAAC;QAC3B,iBAAiB,EAAE,aAAa,CAAC;QACjC,gBAAgB,EAAE,YAAY,CAAC;QAC/B,kBAAkB,EAAE,cAAc,CAAC;QACnC,gBAAgB,EAAE,YAAY,CAAC;QAC/B,oBAAoB,EAAE,gBAAgB,CAAC;QACvC,eAAe,EAAE,WAAW,CAAC;QAC7B,mBAAmB,EAAE,eAAe,CAAC;QACrC,YAAY,EAAE,SAAS,CAAC;KACzB;CACF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -13,6 +13,7 @@ export { MbxPanels } from './entries/panels.js';
13
13
  export { MbxPipButton } from './entries/pip-button.js';
14
14
  export { MbxPlayButton } from './entries/play-button.js';
15
15
  export { MbxQualityMenu } from './entries/quality-menu.js';
16
+ export { MbxRemainingTime } from './entries/remaining-time.js';
16
17
  export { MbxSeekBar } from './entries/seek-bar.js';
17
18
  export { MbxSkipButton } from './entries/skip-button.js';
18
19
  export { MbxSpacer } from './entries/spacer.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA+BA,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAgCA,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC"}
package/dist/style.d.ts CHANGED
@@ -3,6 +3,12 @@
3
3
  * error surface under native controls. Every control carries its own sheet.
4
4
  * Two rules govern them.
5
5
  *
6
+ * The box is black and the picture is centred in it, letterboxed or
7
+ * pillarboxed by `object-fit: contain`, whatever height or ratio the page
8
+ * gives the element: what fullscreen always did, now the default. With no
9
+ * height from the page the stage is as tall as the picture and none of it
10
+ * shows.
11
+ *
6
12
  * Nothing carries `!important`. For normal declarations the outer tree wins
7
13
  * over the shadow tree, so any `::part()` rule the page writes beats these
8
14
  * whatever its specificity; an `!important` here would invert that and make
@@ -11,5 +17,5 @@
11
17
  * Selectors stay at one attribute, and every element is reached through its
12
18
  * `part`, so the names in the stylesheet are the names the page uses.
13
19
  */
14
- export declare const STYLE = "\n:host {\n display: block;\n position: relative;\n outline: none;\n --mbx-surface: #101114;\n --mbx-text: #f2f3f5;\n --mbx-muted: #9aa0a6;\n --mbx-accent: #5b8cff;\n --mbx-error: #ffb4ab;\n --mbx-live: #ff4d4d;\n --mbx-radius: 4px;\n --mbx-gap: 10px;\n --mbx-pad: 8px;\n --mbx-font: system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n}\n[part~=\"stage\"] { position: relative; }\n/* 16:9 until the media says otherwise: a bare video measures 300 by 150 and\n would jump to its size on metadata. Auto first, so the natural ratio wins\n once it is known. */\n::slotted(video) { display: block; width: 100%; aspect-ratio: auto 16 / 9; }\n:host(:focus-visible) [part~=\"stage\"] { outline: 2px solid var(--mbx-accent); outline-offset: -2px; }\n/* Fullscreen goes on the host, so every control the page placed inside\n comes along. The stage takes the height and the video sits in it. The\n height comes from the browser, which gives the fullscreen element the\n whole screen with !important, and not from a display on the host:\n a page's own \"mattebox-player { display: block }\" beats a :host()\n rule, so nothing here may depend on one. The error surface, which sits\n under the stage in the page, goes over its foot instead. */\n:host(:fullscreen) { background: #000; }\n:host(:fullscreen) [part~=\"stage\"] { height: 100%; display: flex; }\n:host(:fullscreen) ::slotted(video) { height: 100%; object-fit: contain; }\n:host(:fullscreen) [part~=\"error\"] { position: absolute; left: 0; right: 0; bottom: 0; }\n:host(:-webkit-full-screen) { background: #000; }\n:host(:-webkit-full-screen) [part~=\"stage\"] { height: 100%; display: flex; }\n:host(:-webkit-full-screen) ::slotted(video) { height: 100%; object-fit: contain; }\n:host(:-webkit-full-screen) [part~=\"error\"] { position: absolute; left: 0; right: 0; bottom: 0; }\n[part~=\"error\"] {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--mbx-gap);\n padding: var(--mbx-pad);\n background: var(--mbx-surface);\n color: var(--mbx-error);\n font: 400 13px/1.4 var(--mbx-font);\n}\n[part~=\"value\"] { color: var(--mbx-accent); }\n[part~=\"button\"] {\n font: inherit;\n color: inherit;\n background: transparent;\n border: 1px solid currentColor;\n border-radius: var(--mbx-radius);\n padding: 2px 6px;\n}\n[hidden] { display: none; }\n";
20
+ export declare const STYLE = "\n:host {\n display: block;\n position: relative;\n outline: none;\n background: #000;\n --mbx-surface: #101114;\n --mbx-text: #f2f3f5;\n --mbx-muted: #9aa0a6;\n --mbx-accent: #5b8cff;\n --mbx-error: #ffb4ab;\n --mbx-live: #ff4d4d;\n --mbx-radius: 4px;\n --mbx-gap: 10px;\n --mbx-pad: 8px;\n --mbx-font: system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n}\n/* The stage is a column as tall as the host, so the picture is centred\n in whatever height the page gives, and the panels row under native\n controls keeps its place below the picture. The height comes from the\n page, or from the browser in fullscreen, and not from a display on the\n host: a page's own \"mattebox-player { display: block }\" beats a :host()\n rule, so nothing here may depend on one. */\n[part~=\"stage\"] {\n position: relative;\n display: flex;\n flex-direction: column;\n justify-content: center;\n height: 100%;\n}\n/* 16:9 until the media says otherwise: a bare video measures 300 by 150 and\n would jump to its size on metadata. Auto first, so the natural ratio wins\n once it is known. Capped at the stage, and contained, so a box shorter\n than the picture letterboxes it and a narrower one pillarboxes it. */\n::slotted(video) {\n display: block;\n width: 100%;\n min-height: 0;\n max-height: 100%;\n object-fit: contain;\n aspect-ratio: auto 16 / 9;\n}\n:host(:focus-visible) [part~=\"stage\"] { outline: 2px solid var(--mbx-accent); outline-offset: -2px; }\n/* Fullscreen goes on the host, so every control the page placed inside\n comes along. The browser gives the host the whole screen with\n !important; the picture takes what the panels row leaves. The error\n surface, which sits under the stage in the page, goes over its foot\n instead. */\n:host(:fullscreen) ::slotted(video) { flex: 1; }\n:host(:fullscreen) [part~=\"error\"] { position: absolute; left: 0; right: 0; bottom: 0; }\n:host(:-webkit-full-screen) ::slotted(video) { flex: 1; }\n:host(:-webkit-full-screen) [part~=\"error\"] { position: absolute; left: 0; right: 0; bottom: 0; }\n[part~=\"error\"] {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--mbx-gap);\n padding: var(--mbx-pad);\n background: var(--mbx-surface);\n color: var(--mbx-error);\n font: 400 13px/1.4 var(--mbx-font);\n}\n[part~=\"value\"] { color: var(--mbx-accent); }\n[part~=\"button\"] {\n font: inherit;\n color: inherit;\n background: transparent;\n border: 1px solid currentColor;\n border-radius: var(--mbx-radius);\n padding: 2px 6px;\n}\n[hidden] { display: none; }\n";
15
21
  //# sourceMappingURL=style.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"style.d.ts","sourceRoot":"","sources":["../src/style.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,KAAK,s0EAyDjB,CAAC"}
1
+ {"version":3,"file":"style.d.ts","sourceRoot":"","sources":["../src/style.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,KAAK,8iFAwEjB,CAAC"}
package/dist/style.js CHANGED
@@ -3,6 +3,12 @@
3
3
  * error surface under native controls. Every control carries its own sheet.
4
4
  * Two rules govern them.
5
5
  *
6
+ * The box is black and the picture is centred in it, letterboxed or
7
+ * pillarboxed by `object-fit: contain`, whatever height or ratio the page
8
+ * gives the element: what fullscreen always did, now the default. With no
9
+ * height from the page the stage is as tall as the picture and none of it
10
+ * shows.
11
+ *
6
12
  * Nothing carries `!important`. For normal declarations the outer tree wins
7
13
  * over the shadow tree, so any `::part()` rule the page writes beats these
8
14
  * whatever its specificity; an `!important` here would invert that and make
@@ -16,6 +22,7 @@ export const STYLE = `
16
22
  display: block;
17
23
  position: relative;
18
24
  outline: none;
25
+ background: #000;
19
26
  --mbx-surface: #101114;
20
27
  --mbx-text: #f2f3f5;
21
28
  --mbx-muted: #9aa0a6;
@@ -27,26 +34,40 @@ export const STYLE = `
27
34
  --mbx-pad: 8px;
28
35
  --mbx-font: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
29
36
  }
30
- [part~="stage"] { position: relative; }
37
+ /* The stage is a column as tall as the host, so the picture is centred
38
+ in whatever height the page gives, and the panels row under native
39
+ controls keeps its place below the picture. The height comes from the
40
+ page, or from the browser in fullscreen, and not from a display on the
41
+ host: a page's own "mattebox-player { display: block }" beats a :host()
42
+ rule, so nothing here may depend on one. */
43
+ [part~="stage"] {
44
+ position: relative;
45
+ display: flex;
46
+ flex-direction: column;
47
+ justify-content: center;
48
+ height: 100%;
49
+ }
31
50
  /* 16:9 until the media says otherwise: a bare video measures 300 by 150 and
32
51
  would jump to its size on metadata. Auto first, so the natural ratio wins
33
- once it is known. */
34
- ::slotted(video) { display: block; width: 100%; aspect-ratio: auto 16 / 9; }
52
+ once it is known. Capped at the stage, and contained, so a box shorter
53
+ than the picture letterboxes it and a narrower one pillarboxes it. */
54
+ ::slotted(video) {
55
+ display: block;
56
+ width: 100%;
57
+ min-height: 0;
58
+ max-height: 100%;
59
+ object-fit: contain;
60
+ aspect-ratio: auto 16 / 9;
61
+ }
35
62
  :host(:focus-visible) [part~="stage"] { outline: 2px solid var(--mbx-accent); outline-offset: -2px; }
36
63
  /* Fullscreen goes on the host, so every control the page placed inside
37
- comes along. The stage takes the height and the video sits in it. The
38
- height comes from the browser, which gives the fullscreen element the
39
- whole screen with !important, and not from a display on the host:
40
- a page's own "mattebox-player { display: block }" beats a :host()
41
- rule, so nothing here may depend on one. The error surface, which sits
42
- under the stage in the page, goes over its foot instead. */
43
- :host(:fullscreen) { background: #000; }
44
- :host(:fullscreen) [part~="stage"] { height: 100%; display: flex; }
45
- :host(:fullscreen) ::slotted(video) { height: 100%; object-fit: contain; }
64
+ comes along. The browser gives the host the whole screen with
65
+ !important; the picture takes what the panels row leaves. The error
66
+ surface, which sits under the stage in the page, goes over its foot
67
+ instead. */
68
+ :host(:fullscreen) ::slotted(video) { flex: 1; }
46
69
  :host(:fullscreen) [part~="error"] { position: absolute; left: 0; right: 0; bottom: 0; }
47
- :host(:-webkit-full-screen) { background: #000; }
48
- :host(:-webkit-full-screen) [part~="stage"] { height: 100%; display: flex; }
49
- :host(:-webkit-full-screen) ::slotted(video) { height: 100%; object-fit: contain; }
70
+ :host(:-webkit-full-screen) ::slotted(video) { flex: 1; }
50
71
  :host(:-webkit-full-screen) [part~="error"] { position: absolute; left: 0; right: 0; bottom: 0; }
51
72
  [part~="error"] {
52
73
  display: flex;
package/dist/style.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"style.js","sourceRoot":"","sources":["../src/style.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyDpB,CAAC"}
1
+ {"version":3,"file":"style.js","sourceRoot":"","sources":["../src/style.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwEpB,CAAC"}
package/dist/tags.d.ts CHANGED
@@ -12,6 +12,7 @@ export declare const START_BUTTON = "mbx-start-button";
12
12
  export declare const ERROR_SCREEN = "mbx-error-screen";
13
13
  export declare const CURRENT_TIME = "mbx-current-time";
14
14
  export declare const DURATION = "mbx-duration";
15
+ export declare const REMAINING_TIME = "mbx-remaining-time";
15
16
  export declare const SEEK_BAR = "mbx-seek-bar";
16
17
  export declare const LIVE_BUTTON = "mbx-live-button";
17
18
  export declare const SPEED_MENU = "mbx-speed-menu";
@@ -1 +1 @@
1
- {"version":3,"file":"tags.d.ts","sourceRoot":"","sources":["../src/tags.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,eAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,eAAO,MAAM,WAAW,oBAAoB,CAAC;AAC7C,eAAO,MAAM,MAAM,eAAe,CAAC;AACnC,eAAO,MAAM,WAAW,oBAAoB,CAAC;AAC7C,eAAO,MAAM,WAAW,oBAAoB,CAAC;AAC7C,eAAO,MAAM,aAAa,sBAAsB,CAAC;AACjD,eAAO,MAAM,WAAW,oBAAoB,CAAC;AAC7C,eAAO,MAAM,UAAU,mBAAmB,CAAC;AAC3C,eAAO,MAAM,iBAAiB,0BAA0B,CAAC;AACzD,eAAO,MAAM,YAAY,qBAAqB,CAAC;AAC/C,eAAO,MAAM,YAAY,qBAAqB,CAAC;AAC/C,eAAO,MAAM,YAAY,qBAAqB,CAAC;AAC/C,eAAO,MAAM,QAAQ,iBAAiB,CAAC;AACvC,eAAO,MAAM,QAAQ,iBAAiB,CAAC;AACvC,eAAO,MAAM,WAAW,oBAAoB,CAAC;AAC7C,eAAO,MAAM,UAAU,mBAAmB,CAAC;AAC3C,eAAO,MAAM,YAAY,qBAAqB,CAAC;AAC/C,eAAO,MAAM,UAAU,mBAAmB,CAAC;AAC3C,eAAO,MAAM,cAAc,uBAAuB,CAAC;AACnD,eAAO,MAAM,SAAS,kBAAkB,CAAC;AACzC,eAAO,MAAM,MAAM,eAAe,CAAC;AACnC,eAAO,MAAM,MAAM,eAAe,CAAC;AACnC,eAAO,MAAM,aAAa,sBAAsB,CAAC"}
1
+ {"version":3,"file":"tags.d.ts","sourceRoot":"","sources":["../src/tags.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,eAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,eAAO,MAAM,WAAW,oBAAoB,CAAC;AAC7C,eAAO,MAAM,MAAM,eAAe,CAAC;AACnC,eAAO,MAAM,WAAW,oBAAoB,CAAC;AAC7C,eAAO,MAAM,WAAW,oBAAoB,CAAC;AAC7C,eAAO,MAAM,aAAa,sBAAsB,CAAC;AACjD,eAAO,MAAM,WAAW,oBAAoB,CAAC;AAC7C,eAAO,MAAM,UAAU,mBAAmB,CAAC;AAC3C,eAAO,MAAM,iBAAiB,0BAA0B,CAAC;AACzD,eAAO,MAAM,YAAY,qBAAqB,CAAC;AAC/C,eAAO,MAAM,YAAY,qBAAqB,CAAC;AAC/C,eAAO,MAAM,YAAY,qBAAqB,CAAC;AAC/C,eAAO,MAAM,QAAQ,iBAAiB,CAAC;AACvC,eAAO,MAAM,cAAc,uBAAuB,CAAC;AACnD,eAAO,MAAM,QAAQ,iBAAiB,CAAC;AACvC,eAAO,MAAM,WAAW,oBAAoB,CAAC;AAC7C,eAAO,MAAM,UAAU,mBAAmB,CAAC;AAC3C,eAAO,MAAM,YAAY,qBAAqB,CAAC;AAC/C,eAAO,MAAM,UAAU,mBAAmB,CAAC;AAC3C,eAAO,MAAM,cAAc,uBAAuB,CAAC;AACnD,eAAO,MAAM,SAAS,kBAAkB,CAAC;AACzC,eAAO,MAAM,MAAM,eAAe,CAAC;AACnC,eAAO,MAAM,MAAM,eAAe,CAAC;AACnC,eAAO,MAAM,aAAa,sBAAsB,CAAC"}