@hh.ru/magritte-ui-video 1.0.1 → 1.0.3

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/index.css CHANGED
@@ -21,7 +21,7 @@
21
21
  .magritte-zp-day-theme{
22
22
  --magritte-color-background-accent-v26-0-2:#7195AF;
23
23
  }
24
- .magritte-player___IHHy5_1-0-1{
24
+ .magritte-player___IHHy5_1-0-3{
25
25
  position:relative;
26
26
  width:100%;
27
27
  overflow:hidden;
@@ -32,23 +32,23 @@
32
32
  -webkit-user-select:none;
33
33
  -webkit-touch-callout:none;
34
34
  }
35
- .magritte-player-fit___TX-6X_1-0-1{
35
+ .magritte-player-fit___TX-6X_1-0-3{
36
36
  height:100%;
37
37
  }
38
- .magritte-player-embedded___GNvRb_1-0-1{
38
+ .magritte-player-embedded___GNvRb_1-0-3{
39
39
  overflow:visible;
40
40
  background-color:transparent;
41
41
  }
42
- .magritte-video___4cS-u_1-0-1{
42
+ .magritte-video___4cS-u_1-0-3{
43
43
  display:block;
44
44
  width:100%;
45
45
  height:100%;
46
46
  object-fit:contain;
47
47
  }
48
- .magritte-video-hidden___agqVq_1-0-1{
48
+ .magritte-video-hidden___agqVq_1-0-3{
49
49
  visibility:hidden;
50
50
  }
51
- .magritte-overlay-button___MIRAP_1-0-1{
51
+ .magritte-overlay-button___MIRAP_1-0-3{
52
52
  position:absolute;
53
53
  top:50%;
54
54
  left:50%;
@@ -58,7 +58,7 @@
58
58
  backdrop-filter:blur(24px);
59
59
  transition:opacity var(--magritte-semantic-animation-time-m-duration-v26-0-2) var(--magritte-semantic-animation-ease-base-timing-function-v26-0-2);
60
60
  }
61
- .magritte-duration___ND1ei_1-0-1{
61
+ .magritte-duration___ND1ei_1-0-3{
62
62
  position:absolute;
63
63
  right:16px;
64
64
  bottom:16px;
@@ -69,7 +69,7 @@
69
69
  backdrop-filter:blur(24px);
70
70
  pointer-events:none;
71
71
  }
72
- .magritte-seek-indicator___VJQBC_1-0-1{
72
+ .magritte-seek-indicator___VJQBC_1-0-3{
73
73
  position:absolute;
74
74
  top:50%;
75
75
  display:flex;
@@ -78,25 +78,25 @@
78
78
  transform:translateY(-50%);
79
79
  pointer-events:none;
80
80
  }
81
- .magritte-seek-indicator-forward___9C5gD_1-0-1{
81
+ .magritte-seek-indicator-forward___9C5gD_1-0-3{
82
82
  right:40px;
83
83
  }
84
- .magritte-seek-indicator-backward___gIZ0E_1-0-1{
84
+ .magritte-seek-indicator-backward___gIZ0E_1-0-3{
85
85
  left:40px;
86
86
  flex-direction:row-reverse;
87
87
  --magritte-ui-video-seek-arrow-shift:16px;
88
88
  }
89
- .magritte-seek-arrows___Ql1eP_1-0-1{
89
+ .magritte-seek-arrows___Ql1eP_1-0-3{
90
90
  position:relative;
91
91
  display:flex;
92
92
  }
93
- .magritte-seek-arrow-incoming___oZxww_1-0-1{
93
+ .magritte-seek-arrow-incoming___oZxww_1-0-3{
94
94
  position:absolute;
95
95
  inset:0;
96
96
  display:flex;
97
- animation:magritte-seek-arrow-in___CB4-s_1-0-1 var(--magritte-semantic-animation-time-m-duration-v26-0-2) var(--magritte-semantic-animation-ease-base-timing-function-v26-0-2) both;
97
+ animation:magritte-seek-arrow-in___CB4-s_1-0-3 var(--magritte-semantic-animation-time-m-duration-v26-0-2) var(--magritte-semantic-animation-ease-base-timing-function-v26-0-2) both;
98
98
  }
99
- @keyframes magritte-seek-arrow-in___CB4-s_1-0-1{
99
+ @keyframes magritte-seek-arrow-in___CB4-s_1-0-3{
100
100
  from{
101
101
  transform:translateX(var(--magritte-ui-video-seek-arrow-shift, -16px));
102
102
  }
@@ -104,7 +104,7 @@
104
104
  transform:translateX(0);
105
105
  }
106
106
  }
107
- .magritte-boost-indicator___ujllx_1-0-1{
107
+ .magritte-boost-indicator___ujllx_1-0-3{
108
108
  position:absolute;
109
109
  top:24px;
110
110
  left:50%;
@@ -118,7 +118,7 @@
118
118
  transform:translateX(-50%);
119
119
  pointer-events:none;
120
120
  }
121
- .magritte-loader___qVu0P_1-0-1{
121
+ .magritte-loader___qVu0P_1-0-3{
122
122
  position:absolute;
123
123
  top:50%;
124
124
  left:50%;
@@ -126,7 +126,7 @@
126
126
  display:flex;
127
127
  pointer-events:none;
128
128
  }
129
- .magritte-error___Rm7uG_1-0-1{
129
+ .magritte-error___Rm7uG_1-0-3{
130
130
  position:absolute;
131
131
  inset:0;
132
132
  display:flex;
@@ -135,12 +135,15 @@
135
135
  padding:16px;
136
136
  overflow:auto;
137
137
  }
138
- .magritte-overlay-button-hidden___AoGFc_1-0-1,
139
- .magritte-controls___pKoSN_1-0-1.magritte-controls-hidden___bePTD_1-0-1{
138
+ .magritte-player-embedded___GNvRb_1-0-3 .magritte-error___Rm7uG_1-0-3{
139
+ touch-action:none;
140
+ }
141
+ .magritte-overlay-button-hidden___AoGFc_1-0-3,
142
+ .magritte-controls___pKoSN_1-0-3.magritte-controls-hidden___bePTD_1-0-3{
140
143
  opacity:0;
141
144
  pointer-events:none;
142
145
  }
143
- .magritte-controls___pKoSN_1-0-1{
146
+ .magritte-controls___pKoSN_1-0-3{
144
147
  display:flex;
145
148
  flex-direction:column;
146
149
  gap:4px;
@@ -148,52 +151,52 @@
148
151
  pointer-events:auto;
149
152
  transition:opacity var(--magritte-semantic-animation-time-m-duration-v26-0-2) var(--magritte-semantic-animation-ease-base-timing-function-v26-0-2);
150
153
  }
151
- .magritte-controls-mobile___tdKGT_1-0-1{
154
+ .magritte-controls-mobile___tdKGT_1-0-3{
152
155
  gap:6px;
153
156
  padding:4px 16px 8px;
154
157
  }
155
- .magritte-controls-overlay___mRsJL_1-0-1{
158
+ .magritte-controls-overlay___mRsJL_1-0-3{
156
159
  position:absolute;
157
160
  right:0;
158
161
  bottom:0;
159
162
  left:0;
160
163
  }
161
- .magritte-controls-row___2sdJg_1-0-1{
164
+ .magritte-controls-row___2sdJg_1-0-3{
162
165
  display:flex;
163
166
  align-items:center;
164
167
  justify-content:space-between;
165
168
  gap:8px;
166
169
  }
167
- .magritte-controls-group-start___yY9ZA_1-0-1,
168
- .magritte-controls-group-end___ziIm8_1-0-1{
170
+ .magritte-controls-group-start___yY9ZA_1-0-3,
171
+ .magritte-controls-group-end___ziIm8_1-0-3{
169
172
  display:flex;
170
173
  align-items:center;
171
174
  min-width:0;
172
175
  }
173
- .magritte-controls-group-start___yY9ZA_1-0-1{
176
+ .magritte-controls-group-start___yY9ZA_1-0-3{
174
177
  gap:12px;
175
178
  }
176
- .magritte-controls-group-end___ziIm8_1-0-1{
179
+ .magritte-controls-group-end___ziIm8_1-0-3{
177
180
  gap:8px;
178
181
  }
179
- .magritte-controls-mobile___tdKGT_1-0-1 .magritte-controls-group-end___ziIm8_1-0-1{
182
+ .magritte-controls-mobile___tdKGT_1-0-3 .magritte-controls-group-end___ziIm8_1-0-3{
180
183
  gap:4px;
181
184
  }
182
- .magritte-control-pill___hK8Mv_1-0-1,
183
- .magritte-controls-time___e9zoz_1-0-1,
184
- .magritte-controls-volume___o4ajR_1-0-1{
185
+ .magritte-control-pill___hK8Mv_1-0-3,
186
+ .magritte-controls-time___e9zoz_1-0-3,
187
+ .magritte-controls-volume___o4ajR_1-0-3{
185
188
  display:flex;
186
189
  align-items:center;
187
190
  justify-content:center;
188
191
  border-radius:999px;
189
192
  backdrop-filter:blur(24px);
190
193
  }
191
- .magritte-control-pill-bare___lU9ev_1-0-1{
194
+ .magritte-control-pill-bare___lU9ev_1-0-3{
192
195
  backdrop-filter:none;
193
196
  }
194
- .magritte-control-button___VJ2Qr_1-0-1,
195
- .magritte-controls-time___e9zoz_1-0-1,
196
- .magritte-controls-volume___o4ajR_1-0-1{
197
+ .magritte-control-button___VJ2Qr_1-0-3,
198
+ .magritte-controls-time___e9zoz_1-0-3,
199
+ .magritte-controls-volume___o4ajR_1-0-3{
197
200
  box-sizing:border-box;
198
201
  min-width:48px;
199
202
  min-height:48px;
@@ -201,27 +204,27 @@
201
204
  border-radius:999px;
202
205
  background-color:var(--magritte-color-background-overlay-v26-0-2);
203
206
  }
204
- .magritte-control-button___VJ2Qr_1-0-1{
207
+ .magritte-control-button___VJ2Qr_1-0-3{
205
208
  display:flex;
206
209
  align-items:center;
207
210
  justify-content:center;
208
211
  border-width:0;
209
212
  cursor:pointer;
210
213
  }
211
- .magritte-control-button___VJ2Qr_1-0-1.focus-visible{
214
+ .magritte-control-button___VJ2Qr_1-0-3.focus-visible{
212
215
  outline:4px solid var(--magritte-color-stroke-state-focused-accessible-v26-0-2);
213
216
  }
214
- .magritte-controls-time___e9zoz_1-0-1{
217
+ .magritte-controls-time___e9zoz_1-0-3{
215
218
  padding:12px 16px;
216
219
  white-space:nowrap;
217
220
  }
218
- .magritte-controls-mobile___tdKGT_1-0-1 .magritte-controls-time___e9zoz_1-0-1{
221
+ .magritte-controls-mobile___tdKGT_1-0-3 .magritte-controls-time___e9zoz_1-0-3{
219
222
  padding:12px;
220
223
  }
221
- .magritte-controls-volume___o4ajR_1-0-1{
224
+ .magritte-controls-volume___o4ajR_1-0-3{
222
225
  gap:6px;
223
226
  }
224
- .magritte-scrubber___-K3ik_1-0-1{
227
+ .magritte-scrubber___-K3ik_1-0-3{
225
228
  position:relative;
226
229
  display:flex;
227
230
  align-items:center;
@@ -232,32 +235,32 @@
232
235
  --magritte-ui-video-scrubber-track-height:4px;
233
236
  --magritte-ui-video-scrubber-thumb-size:8px;
234
237
  }
235
- .magritte-controls-mobile___tdKGT_1-0-1 .magritte-scrubber___-K3ik_1-0-1::before{
238
+ .magritte-controls-mobile___tdKGT_1-0-3 .magritte-scrubber___-K3ik_1-0-3::before{
236
239
  content:'';
237
240
  position:absolute;
238
241
  inset:calc(-1 * 20px) 0 calc(-1 * 6px);
239
242
  }
240
- .magritte-scrubber___-K3ik_1-0-1.focus-visible{
243
+ .magritte-scrubber___-K3ik_1-0-3.focus-visible{
241
244
  outline:4px solid var(--magritte-color-stroke-state-focused-accessible-v26-0-2);
242
245
  border-radius:4px;
243
246
  }
244
- .magritte-scrubber___-K3ik_1-0-1[data-scrubbing]{
247
+ .magritte-scrubber___-K3ik_1-0-3[data-scrubbing]{
245
248
  --magritte-ui-video-scrubber-track-height:6px;
246
249
  --magritte-ui-video-scrubber-thumb-size:12px;
247
250
  }
248
251
  @media (min-width: 1020px){
249
- body.magritte-old-layout .magritte-scrubber___-K3ik_1-0-1:hover{
252
+ body.magritte-old-layout .magritte-scrubber___-K3ik_1-0-3:hover{
250
253
  --magritte-ui-video-scrubber-track-height:6px;
251
254
  --magritte-ui-video-scrubber-thumb-size:12px;
252
255
  }
253
256
  }
254
257
  @media (min-width: 1024px){
255
- body:not(.magritte-old-layout) .magritte-scrubber___-K3ik_1-0-1:hover{
258
+ body:not(.magritte-old-layout) .magritte-scrubber___-K3ik_1-0-3:hover{
256
259
  --magritte-ui-video-scrubber-track-height:6px;
257
260
  --magritte-ui-video-scrubber-thumb-size:12px;
258
261
  }
259
262
  }
260
- .magritte-scrubber-track___ki1zV_1-0-1{
263
+ .magritte-scrubber-track___ki1zV_1-0-3{
261
264
  position:relative;
262
265
  flex-grow:1;
263
266
  height:var(--magritte-ui-video-scrubber-track-height);
@@ -265,21 +268,21 @@
265
268
  border-radius:999px;
266
269
  background-color:var(--magritte-color-background-overlay-v26-0-2);
267
270
  }
268
- .magritte-scrubber-buffered___ufvLe_1-0-1,
269
- .magritte-scrubber-progress___ZIyew_1-0-1{
271
+ .magritte-scrubber-buffered___ufvLe_1-0-3,
272
+ .magritte-scrubber-progress___ZIyew_1-0-3{
270
273
  position:absolute;
271
274
  inset:0;
272
275
  transform-origin:left;
273
276
  }
274
- .magritte-scrubber-buffered___ufvLe_1-0-1{
277
+ .magritte-scrubber-buffered___ufvLe_1-0-3{
275
278
  background-color:var(--magritte-color-stroke-neutral-v26-0-2);
276
279
  transform:scaleX(var(--magritte-ui-video-scrubber-buffered, 0));
277
280
  }
278
- .magritte-scrubber-progress___ZIyew_1-0-1{
281
+ .magritte-scrubber-progress___ZIyew_1-0-3{
279
282
  background-color:var(--magritte-color-background-accent-v26-0-2);
280
283
  transform:scaleX(var(--magritte-ui-video-scrubber-progress, 0));
281
284
  }
282
- .magritte-scrubber-thumb___VXyPP_1-0-1{
285
+ .magritte-scrubber-thumb___VXyPP_1-0-3{
283
286
  position:absolute;
284
287
  top:50%;
285
288
  left:calc(var(--magritte-ui-video-scrubber-progress, 0) * 100%);
@@ -290,7 +293,7 @@
290
293
  transform:translate(-50%, -50%);
291
294
  pointer-events:none;
292
295
  }
293
- .magritte-scrubber-timecode___M-syA_1-0-1{
296
+ .magritte-scrubber-timecode___M-syA_1-0-3{
294
297
  position:absolute;
295
298
  bottom:calc(100% + 8px);
296
299
  left:calc(var(--magritte-ui-video-scrubber-timecode, 0) * 100%);
@@ -302,20 +305,20 @@
302
305
  white-space:nowrap;
303
306
  pointer-events:none;
304
307
  }
305
- .magritte-scrubber___-K3ik_1-0-1[data-scrubbing] .magritte-scrubber-timecode___M-syA_1-0-1{
308
+ .magritte-scrubber___-K3ik_1-0-3[data-scrubbing] .magritte-scrubber-timecode___M-syA_1-0-3{
306
309
  display:flex;
307
310
  }
308
311
  @media (min-width: 1020px){
309
- body.magritte-old-layout .magritte-scrubber___-K3ik_1-0-1:hover .magritte-scrubber-timecode___M-syA_1-0-1{
312
+ body.magritte-old-layout .magritte-scrubber___-K3ik_1-0-3:hover .magritte-scrubber-timecode___M-syA_1-0-3{
310
313
  display:flex;
311
314
  }
312
315
  }
313
316
  @media (min-width: 1024px){
314
- body:not(.magritte-old-layout) .magritte-scrubber___-K3ik_1-0-1:hover .magritte-scrubber-timecode___M-syA_1-0-1{
317
+ body:not(.magritte-old-layout) .magritte-scrubber___-K3ik_1-0-3:hover .magritte-scrubber-timecode___M-syA_1-0-3{
315
318
  display:flex;
316
319
  }
317
320
  }
318
- .magritte-volume-slider___qE2P7_1-0-1{
321
+ .magritte-volume-slider___qE2P7_1-0-3{
319
322
  display:flex;
320
323
  align-items:center;
321
324
  width:64px;
@@ -324,25 +327,25 @@
324
327
  touch-action:none;
325
328
  outline:none;
326
329
  }
327
- .magritte-volume-slider___qE2P7_1-0-1.focus-visible{
330
+ .magritte-volume-slider___qE2P7_1-0-3.focus-visible{
328
331
  outline:4px solid var(--magritte-color-stroke-state-focused-accessible-v26-0-2);
329
332
  border-radius:4px;
330
333
  }
331
- .magritte-volume-track___up8nd_1-0-1{
334
+ .magritte-volume-track___up8nd_1-0-3{
332
335
  position:relative;
333
336
  flex-grow:1;
334
337
  height:4px;
335
338
  border-radius:999px;
336
339
  background-color:var(--magritte-color-background-overlay-v26-0-2);
337
340
  }
338
- .magritte-volume-value___aUSuU_1-0-1{
341
+ .magritte-volume-value___aUSuU_1-0-3{
339
342
  position:relative;
340
343
  width:calc(var(--magritte-ui-video-volume, 1) * 100%);
341
344
  height:100%;
342
345
  border-radius:999px;
343
346
  background-color:var(--magritte-color-stroke-neutral-v26-0-2);
344
347
  }
345
- .magritte-volume-value___aUSuU_1-0-1::after{
348
+ .magritte-volume-value___aUSuU_1-0-3::after{
346
349
  content:'';
347
350
  position:absolute;
348
351
  top:50%;
package/index.js CHANGED
@@ -11,7 +11,7 @@ import '@hh.ru/magritte-ui-icon/icon';
11
11
  import '@hh.ru/magritte-ui-typography';
12
12
  import './internal/ControlButton.js';
13
13
  import '@hh.ru/magritte-ui-tooltip';
14
- import './video-C-iklAZX.js';
14
+ import './video-B0giLQLN.js';
15
15
  import './internal/Scrubber.js';
16
16
  import './internal/formatTime.js';
17
17
  import './internal/useSliderPointer.js';
@@ -30,4 +30,5 @@ import '@hh.ru/magritte-ui-placeholder';
30
30
  import './internal/SeekIndicator.js';
31
31
  import './internal/store.js';
32
32
  import './internal/useCanvasGestures.js';
33
+ import './internal/useViewportVisibility.js';
33
34
  //# sourceMappingURL=index.js.map
package/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -3,7 +3,7 @@ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
3
3
  import { useRef } from 'react';
4
4
  import classnames from 'classnames';
5
5
  import { TooltipHover } from '@hh.ru/magritte-ui-tooltip';
6
- import { s as styles } from '../video-C-iklAZX.js';
6
+ import { s as styles } from '../video-B0giLQLN.js';
7
7
 
8
8
  const ControlButton = ({
9
9
  control,
@@ -6,7 +6,7 @@ import { Text } from '@hh.ru/magritte-ui-typography';
6
6
  import { formatTime, formatDuration } from './formatTime.js';
7
7
  import { useSliderPointer } from './useSliderPointer.js';
8
8
  import { SEEK_STEP_SECONDS } from './useVideoHotkeys.js';
9
- import { s as styles } from '../video-C-iklAZX.js';
9
+ import { s as styles } from '../video-B0giLQLN.js';
10
10
  import '@hh.ru/magritte-common-keyboard';
11
11
 
12
12
  const KEYBOARD_SEEK_STEP = SEEK_STEP_SECONDS;
@@ -3,7 +3,7 @@ import { jsxs, jsx } from 'react/jsx-runtime';
3
3
  import classnames from 'classnames';
4
4
  import { ChevronRightOutlinedSize24, ChevronLeftOutlinedSize24 } from '@hh.ru/magritte-ui-icon/icon';
5
5
  import { Text } from '@hh.ru/magritte-ui-typography';
6
- import { s as styles } from '../video-C-iklAZX.js';
6
+ import { s as styles } from '../video-B0giLQLN.js';
7
7
 
8
8
  const SeekIndicator = ({ direction, seconds, count }) => {
9
9
  const forward = direction > 0;
@@ -3,7 +3,7 @@ import { jsx } from 'react/jsx-runtime';
3
3
  import { useTranslation } from '@hh.ru/magritte-common-use-translation';
4
4
  import { useVideoState } from './context.js';
5
5
  import { useSliderPointer } from './useSliderPointer.js';
6
- import { s as styles } from '../video-C-iklAZX.js';
6
+ import { s as styles } from '../video-B0giLQLN.js';
7
7
  import 'react';
8
8
 
9
9
  const KEYBOARD_VOLUME_STEP = 0.1;
@@ -5,6 +5,10 @@ export interface VideoContextValue {
5
5
  preview?: string;
6
6
  /** Длительность из пропсов — до загрузки метаданных */
7
7
  duration?: number;
8
+ /** `false` — неинтерактивный режим: без кнопок, контролов, жестов и горячих клавиш */
9
+ interactive: boolean;
10
+ /** Плеер сообщает стору свою видимость во вьюпорте (автовоспроизведение, автопауза) */
11
+ observeViewport: boolean;
8
12
  }
9
13
  export declare const VideoContext: import("react").Context<VideoContextValue | null>;
10
14
  export declare const useVideoContext: (componentName: string) => VideoContextValue;
@@ -18,5 +22,10 @@ export declare const VideoPlayerContext: import("react").Context<boolean>;
18
22
  */
19
23
  export interface VideoPlayerHost {
20
24
  renderVideo: (video: ReactElement) => ReactNode;
25
+ /**
26
+ * Атрибуты кнопки повтора на экране ошибки: в ошибке тапы по плееру обрабатывает хост, а по ним он отличает
27
+ * нажатие на кнопку от тапа по видео
28
+ */
29
+ retryButtonProps?: Record<`data-${string}`, string | boolean>;
21
30
  }
22
31
  export declare const VideoPlayerHostContext: import("react").Context<VideoPlayerHost | null>;
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sources":["src/internal/context.ts"],"sourcesContent":["import { createContext, type ReactElement, type ReactNode, useContext, useSyncExternalStore } from 'react';\n\nimport { type VideoState, type VideoStore } from '@hh.ru/magritte-ui-video/internal/store';\n\nexport interface VideoContextValue {\n store: VideoStore;\n preview?: string;\n /** Длительность из пропсов — до загрузки метаданных */\n duration?: number;\n}\n\nexport const VideoContext = createContext<VideoContextValue | null>(null);\n\nexport const useVideoContext = (componentName: string): VideoContextValue => {\n const context = useContext(VideoContext);\n if (!context) {\n throw new Error(`${componentName} must be used within <Video>`);\n }\n return context;\n};\n\n/** Подписка на часть состояния: компонент перерендеривается, только когда она меняется */\nexport const useVideoState = <T>(store: VideoStore, selector: (state: VideoState) => T): T => {\n const getSnapshot = () => selector(store.getState());\n return useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);\n};\n\n/** Части видео внутри `Video.Player` (например, `Video.Controls`) раскладываются поверх видео */\nexport const VideoPlayerContext = createContext(false);\n\n/**\n * Встраивание `Video.Player` в другой компонент дизайн-системы (вьювер медиа): хост оборачивает `<video>` в свою\n * разметку (например, трансформацию зума), оверлеи плеера остаются без неё. Только для внутреннего использования\n */\nexport interface VideoPlayerHost {\n renderVideo: (video: ReactElement) => ReactNode;\n}\n\nexport const VideoPlayerHostContext = createContext<VideoPlayerHost | null>(null);\n"],"names":[],"mappings":";;AAWa,MAAA,YAAA,GAAe,cAAwC,IAAI,EAAA;AAE3D,MAAA,eAAA,GAAkB,CAAC,aAA6C,KAAA;AACzE,EAAM,MAAA,OAAA,GAAU,WAAW,YAAY,CAAA,CAAA;AACvC,EAAA,IAAI,CAAC,OAAS,EAAA;AACV,IAAA,MAAM,IAAI,KAAA,CAAM,CAAG,EAAA,aAAa,CAA8B,4BAAA,CAAA,CAAA,CAAA;AAAA,GAClE;AACA,EAAO,OAAA,OAAA,CAAA;AACX,EAAA;AAGa,MAAA,aAAA,GAAgB,CAAI,KAAA,EAAmB,QAA0C,KAAA;AAC1F,EAAA,MAAM,WAAc,GAAA,MAAM,QAAS,CAAA,KAAA,CAAM,UAAU,CAAA,CAAA;AACnD,EAAA,OAAO,oBAAqB,CAAA,KAAA,CAAM,SAAW,EAAA,WAAA,EAAa,WAAW,CAAA,CAAA;AACzE,EAAA;AAGa,MAAA,kBAAA,GAAqB,cAAc,KAAK,EAAA;AAUxC,MAAA,sBAAA,GAAyB,cAAsC,IAAI;;;;"}
1
+ {"version":3,"file":"context.js","sources":["src/internal/context.ts"],"sourcesContent":["import { createContext, type ReactElement, type ReactNode, useContext, useSyncExternalStore } from 'react';\n\nimport { type VideoState, type VideoStore } from '@hh.ru/magritte-ui-video/internal/store';\n\nexport interface VideoContextValue {\n store: VideoStore;\n preview?: string;\n /** Длительность из пропсов — до загрузки метаданных */\n duration?: number;\n /** `false` — неинтерактивный режим: без кнопок, контролов, жестов и горячих клавиш */\n interactive: boolean;\n /** Плеер сообщает стору свою видимость во вьюпорте (автовоспроизведение, автопауза) */\n observeViewport: boolean;\n}\n\nexport const VideoContext = createContext<VideoContextValue | null>(null);\n\nexport const useVideoContext = (componentName: string): VideoContextValue => {\n const context = useContext(VideoContext);\n if (!context) {\n throw new Error(`${componentName} must be used within <Video>`);\n }\n return context;\n};\n\n/** Подписка на часть состояния: компонент перерендеривается, только когда она меняется */\nexport const useVideoState = <T>(store: VideoStore, selector: (state: VideoState) => T): T => {\n const getSnapshot = () => selector(store.getState());\n return useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);\n};\n\n/** Части видео внутри `Video.Player` (например, `Video.Controls`) раскладываются поверх видео */\nexport const VideoPlayerContext = createContext(false);\n\n/**\n * Встраивание `Video.Player` в другой компонент дизайн-системы (вьювер медиа): хост оборачивает `<video>` в свою\n * разметку (например, трансформацию зума), оверлеи плеера остаются без неё. Только для внутреннего использования\n */\nexport interface VideoPlayerHost {\n renderVideo: (video: ReactElement) => ReactNode;\n /**\n * Атрибуты кнопки повтора на экране ошибки: в ошибке тапы по плееру обрабатывает хост, а по ним он отличает\n * нажатие на кнопку от тапа по видео\n */\n retryButtonProps?: Record<`data-${string}`, string | boolean>;\n}\n\nexport const VideoPlayerHostContext = createContext<VideoPlayerHost | null>(null);\n"],"names":[],"mappings":";;AAea,MAAA,YAAA,GAAe,cAAwC,IAAI,EAAA;AAE3D,MAAA,eAAA,GAAkB,CAAC,aAA6C,KAAA;AACzE,EAAM,MAAA,OAAA,GAAU,WAAW,YAAY,CAAA,CAAA;AACvC,EAAA,IAAI,CAAC,OAAS,EAAA;AACV,IAAA,MAAM,IAAI,KAAA,CAAM,CAAG,EAAA,aAAa,CAA8B,4BAAA,CAAA,CAAA,CAAA;AAAA,GAClE;AACA,EAAO,OAAA,OAAA,CAAA;AACX,EAAA;AAGa,MAAA,aAAA,GAAgB,CAAI,KAAA,EAAmB,QAA0C,KAAA;AAC1F,EAAA,MAAM,WAAc,GAAA,MAAM,QAAS,CAAA,KAAA,CAAM,UAAU,CAAA,CAAA;AACnD,EAAA,OAAO,oBAAqB,CAAA,KAAA,CAAM,SAAW,EAAA,WAAA,EAAa,WAAW,CAAA,CAAA;AACzE,EAAA;AAGa,MAAA,kBAAA,GAAqB,cAAc,KAAK,EAAA;AAexC,MAAA,sBAAA,GAAyB,cAAsC,IAAI;;;;"}
@@ -3,6 +3,11 @@ export interface VideoState {
3
3
  status: VideoStatus;
4
4
  /** Воспроизведение запущено, но видео ждёт данные */
5
5
  waiting: boolean;
6
+ /**
7
+ * Повторная попытка после ошибки: до начала воспроизведения видео и превью не показываются — лоадер на тёмном
8
+ * фоне, а не мелькание превью перед новой ошибкой
9
+ */
10
+ retrying: boolean;
6
11
  /** Длительность из метаданных; `null`, пока неизвестна или бесконечна (live) */
7
12
  duration: number | null;
8
13
  /** Натуральный размер видео из метаданных */
@@ -27,6 +32,25 @@ export interface VideoState {
27
32
  /** Куда переносится индикатор ускорения вне fullscreen; `null` — вверху плеера */
28
33
  indicatorContainer: HTMLElement | null;
29
34
  }
35
+ /**
36
+ * Исключение из правил владельца видимости контролов: `shown` — показать и не прятать (фокус с клавиатуры в контролах,
37
+ * перетаскивание бегунка), `hidden` — спрятать и не показывать от активности (ускорение удержанием на desktop),
38
+ * `null` — исключений нет
39
+ */
40
+ export type VideoControlsPin = 'shown' | 'hidden' | null;
41
+ /**
42
+ * Внешний владелец видимости контролов (тулбары вьювера медиа). Пока он подключён, контролы показываются и прячутся
43
+ * только по нему: собственного таймера у плеера нет, активность и тапы по видео передаются ему
44
+ */
45
+ export interface VideoControlsVisibilityHost {
46
+ isVisible: () => boolean;
47
+ subscribe: (listener: (visible: boolean) => void) => VoidFunction;
48
+ /** Активность пользователя в плеере: движение мыши, нажатия, горячие клавиши */
49
+ notifyActivity: VoidFunction;
50
+ /** Тап по видео (mobile) */
51
+ toggle: VoidFunction;
52
+ setPin: (pin: VideoControlsPin) => void;
53
+ }
30
54
  export interface VideoTime {
31
55
  current: number;
32
56
  duration: number;
@@ -43,11 +67,23 @@ export interface VideoSourceOptions {
43
67
  sources?: VideoSource[];
44
68
  }
45
69
  type VideoCallbacks = Pick<VideoProps, 'onPlay' | 'onPause' | 'onEnded' | 'onError'>;
70
+ /** Режимы воспроизведения из пропсов `Video`; `interactive={false}` включает autoPlay и loop */
71
+ export interface VideoPlaybackOptions {
72
+ autoPlay: boolean;
73
+ autoPause: boolean;
74
+ loop: boolean;
75
+ interactive: boolean;
76
+ }
46
77
  export interface VideoStoreOptions extends VideoSourceOptions {
47
78
  /** Актуальные колбэки и attachMedia читаются в момент события — без пересоздания стора */
48
79
  getCallbacks: () => VideoCallbacks & {
49
80
  attachMedia?: VideoAttachMedia;
50
81
  };
82
+ /** Актуальные режимы воспроизведения; после их смены стор надо уведомить через `syncOptions` */
83
+ getOptions: () => VideoPlaybackOptions;
84
+ /** Звук и громкость на старте */
85
+ defaultMuted?: boolean;
86
+ defaultVolume?: number;
51
87
  }
52
88
  export interface VideoStore {
53
89
  getState: () => VideoState;
@@ -66,6 +102,13 @@ export interface VideoStore {
66
102
  seek: (seconds: number) => void;
67
103
  /** Перемотка относительно текущей позиции */
68
104
  seekBy: (seconds: number) => void;
105
+ /**
106
+ * Видимость плеера во вьюпорте (видно не меньше 10%): по ней работают автовоспроизведение и автопауза.
107
+ * `null` — неизвестно (плеер размонтирован)
108
+ */
109
+ setVisible: (visible: boolean | null) => void;
110
+ /** Режимы воспроизведения из пропсов изменились */
111
+ syncOptions: VoidFunction;
69
112
  /** Повторная загрузка после ошибки с запуском воспроизведения */
70
113
  retry: VoidFunction;
71
114
  /** play, если видео на паузе, закончилось или ещё не запускалось; иначе pause */
@@ -83,6 +126,8 @@ export interface VideoStore {
83
126
  setHideWhenPaused: (hide: boolean) => void;
84
127
  /** Пока удержание активно (фокус внутри контролов, перетаскивание ползунка), контролы не прячутся */
85
128
  holdControls: (hold: boolean) => void;
129
+ /** Подключить внешнего владельца видимости контролов; `null` — плеер снова управляет ими сам */
130
+ setControlsVisibilityHost: (host: VideoControlsVisibilityHost | null) => void;
86
131
  /** Callback ref для корня `Video.Player` — элемента, который разворачивается на весь экран */
87
132
  attachRoot: (element: HTMLElement | null) => void;
88
133
  getRoot: () => HTMLElement | null;
@@ -105,5 +150,5 @@ export declare const PLAYBACK_RATES: number[];
105
150
  export declare const BOOST_RATE = 2;
106
151
  /** Задержка автоскрытия контролов по спеке */
107
152
  export declare const CONTROLS_HIDE_DELAY_MS = 4000;
108
- export declare const createVideoStore: ({ getCallbacks, ...initialSource }: VideoStoreOptions) => VideoStore;
153
+ export declare const createVideoStore: ({ getCallbacks, getOptions, defaultMuted, defaultVolume, ...initialSource }: VideoStoreOptions) => VideoStore;
109
154
  export {};