@folklore/hooks 0.0.83 → 0.0.86

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/cjs.js DELETED
@@ -1,1960 +0,0 @@
1
- 'use strict';
2
-
3
- var raf = require('raf');
4
- var react = require('react');
5
- var services = require('@folklore/services');
6
- var createDebug = require('debug');
7
- var EventsManager = require('@folklore/events');
8
- var cancelablePromise = require('cancelable-promise');
9
- var isNumber = require('lodash/isNumber');
10
-
11
- function useCounter(desiredValue, {
12
- disabled = false,
13
- maxDuration = 2000,
14
- speed = 1 / 10,
15
- initialValue = null
16
- }) {
17
- const [currentValue, setCurrentValue] = react.useState(initialValue !== null ? initialValue : desiredValue);
18
- react.useEffect(() => {
19
- if (initialValue !== null && !disabled) {
20
- setCurrentValue(initialValue);
21
- }
22
- }, [initialValue, disabled]);
23
- react.useEffect(() => {
24
- const finalCurrentValue = disabled ? desiredValue : currentValue;
25
- let animationFrame = null;
26
- let startTime = null;
27
- let duration = 0;
28
- let canceled = false;
29
- const startValue = finalCurrentValue;
30
- const delta = desiredValue - startValue;
31
- function loop() {
32
- if (canceled) {
33
- return;
34
- }
35
- const currentTime = Date.now();
36
- const elapsedTime = currentTime - startTime;
37
- const progress = Math.min(elapsedTime / duration, 1);
38
- const newValue = Math.round(startValue + progress * delta);
39
- setCurrentValue(newValue);
40
- if (newValue !== desiredValue) {
41
- animationFrame = raf(loop);
42
- }
43
- }
44
- if (delta !== 0) {
45
- duration = Math.min(maxDuration, Math.abs(delta) * speed * 1000);
46
- startTime = Date.now();
47
- animationFrame = raf(loop);
48
- } else if (disabled) {
49
- setCurrentValue(desiredValue);
50
- }
51
- return () => {
52
- canceled = true;
53
- if (animationFrame !== null) {
54
- raf.cancel(animationFrame);
55
- }
56
- };
57
- }, [desiredValue, disabled, initialValue, maxDuration, speed]);
58
- return currentValue;
59
- }
60
-
61
- const NO_PLAYER_ERROR$3 = new Error('No player');
62
- function useDailymotionPlayer(id = null, params = {}) {
63
- const {
64
- width = 0,
65
- height = 0,
66
- duration = 0,
67
- muted: providedMuted = false,
68
- initialMuted = false,
69
- start = 0,
70
- embedPlayerId = null,
71
- onTimeUpdate: customOnTimeUpdate = null,
72
- getVideoId = url => {
73
- if (url === null || url.match(/^https?:/) === null) {
74
- return url;
75
- }
76
- let match = url.match(/\/video\/([^/?]+)/);
77
- if (match !== null) {
78
- return match[1];
79
- }
80
- match = url.match(/video=([^/?&]+)/);
81
- if (match !== null) {
82
- return match[1];
83
- }
84
- return null;
85
- }
86
- } = params;
87
- const debug = react.useMemo(() => createDebug('folklore:video:dailymotion'), []);
88
- const [apiLoaded, setApiLoaded] = react.useState(typeof window !== 'undefined' && typeof window.dailymotion !== 'undefined');
89
- const [playerReady, setPlayerReady] = react.useState(false);
90
- const [loaded, setLoaded] = react.useState(false);
91
- const apiRef = react.useRef(typeof window !== 'undefined' && typeof window.dailymotion !== 'undefined' ? window.dailymotion : null);
92
- const ready = apiLoaded && playerReady;
93
- const videoId = react.useMemo(() => getVideoId(id), [id]);
94
- const elementRef = react.useRef(null);
95
- const playerRef = react.useRef(null);
96
- const playerElementRef = react.useRef(elementRef.current);
97
- if (elementRef.current !== null && playerElementRef.current === null) {
98
- playerElementRef.current = elementRef.current;
99
- }
100
- const elementHasChanged = elementRef.current !== playerElementRef.current;
101
- const [muted, setMuted] = react.useState(initialMuted || providedMuted);
102
- const [volume, setVolumeState] = react.useState(initialMuted || providedMuted ? 0 : 1);
103
- const [currentTime, setCurrentTime] = react.useState(0);
104
- const [playState, setPlayState] = react.useState({
105
- playing: false,
106
- paused: false,
107
- ended: false,
108
- buffering: false,
109
- adPlaying: false
110
- });
111
- const [metadata, setMetadata] = react.useState({
112
- width,
113
- height,
114
- duration
115
- });
116
-
117
- // Load SDK
118
- react.useEffect(() => {
119
- let canceled = false;
120
- if (!apiLoaded && videoId !== null) {
121
- debug('Load API');
122
- services.loadDailymotion({
123
- url: embedPlayerId !== null ? `https://geo.dailymotion.com/libs/player/${embedPlayerId}.js` : 'https://geo.dailymotion.com/libs/player.js',
124
- callback: null
125
- }).then(api => {
126
- if (!canceled) {
127
- apiRef.current = api;
128
- setApiLoaded(true);
129
- debug('API Loaded');
130
- }
131
- });
132
- }
133
- return () => {
134
- canceled = true;
135
- };
136
- }, [videoId, apiLoaded, setApiLoaded, embedPlayerId]);
137
-
138
- // Create or update player
139
- react.useEffect(() => {
140
- const {
141
- current: dailymotion = null
142
- } = apiRef;
143
- const {
144
- current: currentPlayer = null
145
- } = playerRef;
146
- const {
147
- current: element = null
148
- } = elementRef;
149
- if (!apiLoaded || videoId === null || element === null) {
150
- return;
151
- }
152
- const playerParams = {
153
- startTime: start,
154
- aspectRatio: 'inherit'
155
- };
156
- let player = currentPlayer;
157
- if (player !== null) {
158
- player.loadContent({
159
- video: videoId
160
- });
161
- debug('Load video [ID: %s]', videoId);
162
- } else {
163
- element.id = `dailymotion-player-${videoId}-${Date.now()}`;
164
- player = dailymotion.createPlayer(element.id, {
165
- video: videoId,
166
- width,
167
- height,
168
- params: playerParams
169
- }).then(newPlayer => {
170
- debug('Player ready [ID: %s]', videoId);
171
- setPlayerReady(true);
172
- playerRef.current = newPlayer;
173
- });
174
- debug('Create player [ID: %s]', videoId);
175
- }
176
- playerElementRef.current = element;
177
- }, [apiLoaded, elementHasChanged, videoId, width, height, start]);
178
- react.useEffect(() => {
179
- const {
180
- current: player = null
181
- } = playerRef;
182
- const {
183
- current: dailymotion = null
184
- } = apiRef;
185
- if (player === null) {
186
- return () => {};
187
- }
188
- let currentPlayState = playState;
189
- let currentMetadata = metadata;
190
- function onPlaybackReady() {
191
- setLoaded(true);
192
- debug('onPlaybackReady [ID: %s]', videoId);
193
- }
194
- function onDurationChange({
195
- videoTime,
196
- videoDuration
197
- }) {
198
- currentMetadata = {
199
- ...currentMetadata,
200
- duration: videoDuration
201
- };
202
- setMetadata(currentMetadata);
203
- setCurrentTime(videoTime);
204
- debug('onDurationChange [ID: %s]', videoId);
205
- }
206
- function onVolumeChange({
207
- playerIsMuted,
208
- playerVolume,
209
- videoTime
210
- }) {
211
- setMuted(playerIsMuted);
212
- setVolumeState(playerVolume);
213
- setCurrentTime(videoTime);
214
- debug('onVolumeChange [ID: %s]', videoId);
215
- }
216
- function onPlay({
217
- videoTime
218
- }) {
219
- currentPlayState = {
220
- ...currentPlayState,
221
- playing: true,
222
- paused: false,
223
- ended: false
224
- };
225
- setPlayState(currentPlayState);
226
- setCurrentTime(videoTime);
227
- debug('onPlay [ID: %s]', videoId);
228
- }
229
- function onPause({
230
- videoTime
231
- }) {
232
- currentPlayState = {
233
- ...currentPlayState,
234
- playing: false,
235
- paused: true,
236
- ended: false
237
- };
238
- setPlayState(currentPlayState);
239
- setCurrentTime(videoTime);
240
- debug('onPause [ID: %s]', videoId);
241
- }
242
- function onEnd({
243
- videoTime
244
- }) {
245
- currentPlayState = {
246
- ...currentPlayState,
247
- playing: false,
248
- paused: false,
249
- ended: true
250
- };
251
- setPlayState(currentPlayState);
252
- setCurrentTime(videoTime);
253
- debug('onEnd [ID: %s]', videoId);
254
- }
255
- function onPlaying({
256
- videoTime
257
- }) {
258
- currentPlayState = {
259
- ...currentPlayState,
260
- buffering: false
261
- };
262
- setPlayState(currentPlayState);
263
- setCurrentTime(videoTime);
264
- debug('onPlaying [ID: %s]', videoId);
265
- }
266
- function onWaiting({
267
- videoTime
268
- }) {
269
- currentPlayState = {
270
- ...currentPlayState,
271
- buffering: true
272
- };
273
- setPlayState(currentPlayState);
274
- setCurrentTime(videoTime);
275
- debug('onWaiting [ID: %s]', videoId);
276
- }
277
- function onTimeChange({
278
- videoTime
279
- }) {
280
- setCurrentTime(videoTime);
281
- }
282
- function onAdStart() {
283
- currentPlayState = {
284
- ...currentPlayState,
285
- adPlaying: true
286
- };
287
- setPlayState(currentPlayState);
288
- debug('onAdStart [ID: %s]', videoId);
289
- }
290
- function onAdEnd() {
291
- currentPlayState = {
292
- ...currentPlayState,
293
- adPlaying: false
294
- };
295
- setPlayState(currentPlayState);
296
- debug('onAdEnd [ID: %s]', videoId);
297
- }
298
- player.on(dailymotion.events.PLAYER_CRITICALPATHREADY, onPlaybackReady);
299
- player.on(dailymotion.events.VIDEO_DURATIONCHANGE, onDurationChange);
300
- player.on(dailymotion.events.PLAYER_VOLUMECHANGE, onVolumeChange);
301
- player.on(dailymotion.events.VIDEO_PLAY, onPlay);
302
- player.on(dailymotion.events.VIDEO_PAUSE, onPause);
303
- player.on(dailymotion.events.VIDEO_END, onEnd);
304
- player.on(dailymotion.events.VIDEO_PLAYING, onPlaying);
305
- player.on(dailymotion.events.VIDEO_BUFFERING, onWaiting);
306
- player.on(dailymotion.events.VIDEO_TIMECHANGE, onTimeChange);
307
- player.on(dailymotion.events.AD_START, onAdStart);
308
- player.on(dailymotion.events.AD_END, onAdEnd);
309
- return () => {
310
- player.off(dailymotion.events.PLAYER_CRITICALPATHREADY, onPlaybackReady);
311
- player.off(dailymotion.events.VIDEO_DURATIONCHANGE, onDurationChange);
312
- player.off(dailymotion.events.PLAYER_VOLUMECHANGE, onVolumeChange);
313
- player.off(dailymotion.events.VIDEO_PLAY, onPlay);
314
- player.off(dailymotion.events.VIDEO_PAUSE, onPause);
315
- player.off(dailymotion.events.VIDEO_END, onEnd);
316
- player.off(dailymotion.events.VIDEO_PLAYING, onPlaying);
317
- player.off(dailymotion.events.VIDEO_BUFFERING, onWaiting);
318
- player.off(dailymotion.events.VIDEO_TIMECHANGE, onTimeChange);
319
- player.off(dailymotion.events.AD_START, onAdStart);
320
- player.off(dailymotion.events.AD_END, onAdEnd);
321
- };
322
- }, [playerRef.current, playerReady, videoId, setLoaded, setPlayState, setMetadata, setVolumeState, setMuted]);
323
- const play = react.useCallback(() => {
324
- const {
325
- current: player
326
- } = playerRef;
327
- return player !== null ? player.play() : Promise.reject(NO_PLAYER_ERROR$3);
328
- }, []);
329
- const pause = react.useCallback(() => {
330
- const {
331
- current: player
332
- } = playerRef;
333
- return player !== null ? player.pause() : Promise.reject(NO_PLAYER_ERROR$3);
334
- }, []);
335
- const setVolume = react.useCallback(newVolume => {
336
- const {
337
- current: player
338
- } = playerRef;
339
- return player !== null ? player.setVolume(newVolume) : Promise.reject(NO_PLAYER_ERROR$3);
340
- }, []);
341
- const mute = react.useCallback(() => {
342
- const {
343
- current: player
344
- } = playerRef;
345
- return player !== null ? player.setMute(true) : Promise.reject(NO_PLAYER_ERROR$3);
346
- }, []);
347
- const unmute = react.useCallback(() => {
348
- const {
349
- current: player
350
- } = playerRef;
351
- return player !== null ? player.setMute(false) : Promise.reject(NO_PLAYER_ERROR$3);
352
- }, []);
353
- const seek = react.useCallback(time => {
354
- const {
355
- current: player
356
- } = playerRef;
357
- return player !== null ? player.seek(time) : Promise.reject(NO_PLAYER_ERROR$3);
358
- }, []);
359
- react.useEffect(() => {
360
- if (customOnTimeUpdate !== null) {
361
- customOnTimeUpdate(currentTime);
362
- }
363
- }, [currentTime, customOnTimeUpdate]);
364
- return {
365
- ref: elementRef,
366
- player: playerRef.current,
367
- ready,
368
- play,
369
- pause,
370
- mute,
371
- unmute,
372
- setVolume,
373
- seek,
374
- currentTime,
375
- loaded,
376
- muted,
377
- volume,
378
- ...metadata,
379
- ...playState
380
- };
381
- }
382
-
383
- const eventsManager$1 = typeof document !== 'undefined' ? new EventsManager(document) : null;
384
- function useDocumentEvent(event, callback) {
385
- react.useEffect(() => {
386
- if (eventsManager$1 !== null && callback !== null) {
387
- eventsManager$1.subscribe(event, callback);
388
- }
389
- return () => {
390
- if (eventsManager$1 !== null && callback !== null) {
391
- eventsManager$1.unsubscribe(event, callback);
392
- }
393
- };
394
- }, [event, callback]);
395
- }
396
-
397
- const observersCache = new Map();
398
- function getOptionsKey({
399
- root = null,
400
- rootMargin,
401
- threshold = null
402
- }) {
403
- return `root_${root}_rootMargin_${rootMargin || null}_threshold_${threshold}`;
404
- }
405
- function createObserver(Observer, options = {}) {
406
- let subscribers = [];
407
- const addSubscriber = (element, callback) => {
408
- const currentSubscriber = subscribers.find(it => it.element === element) || null;
409
- if (currentSubscriber !== null) {
410
- return subscribers.map(it => it.element === element && it.callbacks.indexOf(callback) === -1 ? {
411
- ...it,
412
- callbacks: [...it.callbacks, callback]
413
- } : it).filter(it => it.callbacks.length > 0);
414
- }
415
- return [...subscribers, {
416
- element,
417
- callbacks: [callback]
418
- }];
419
- };
420
- const removeSubscriber = (element, callback) => subscribers.map(it => it.element === element ? {
421
- ...it,
422
- callbacks: it.callbacks.filter(subCallback => subCallback !== callback)
423
- } : it).filter(it => it.callbacks.length > 0);
424
- const onUpdate = entries => {
425
- entries.forEach(entry => {
426
- subscribers.forEach(({
427
- element,
428
- callbacks
429
- }) => {
430
- if (element === entry.target) {
431
- callbacks.forEach(callback => {
432
- callback(entry);
433
- });
434
- }
435
- });
436
- });
437
- };
438
- const observer = new Observer(onUpdate, options);
439
- const unsubscribe = (element, callback = null) => {
440
- subscribers = removeSubscriber(element, callback);
441
- if (typeof observer.unobserve === 'undefined') {
442
- observer.disconnect();
443
- subscribers.forEach(subscriber => {
444
- observer.observe(subscriber.element);
445
- });
446
- return;
447
- }
448
- const currentSubscriber = subscribers.find(it => it.element === element) || null;
449
- if (currentSubscriber === null) {
450
- observer.unobserve(element);
451
- }
452
- };
453
- const subscribe = (element, callback) => {
454
- const currentSubscriber = subscribers.find(it => it.element === element) || null;
455
- subscribers = addSubscriber(element, callback);
456
- if (currentSubscriber === null) {
457
- observer.observe(element);
458
- }
459
- };
460
- return {
461
- subscribe,
462
- unsubscribe,
463
- observer
464
- };
465
- }
466
- function getObserver(Observer = null, options = {}) {
467
- if (Observer === null) {
468
- return {
469
- observer: null,
470
- subscribe: () => {},
471
- unsubscribe: () => {}
472
- };
473
- }
474
- const observerKey = getOptionsKey(options);
475
- if (!observersCache.has(Observer)) {
476
- observersCache.set(Observer, {});
477
- }
478
- const observers = observersCache.get(Observer);
479
- if (typeof observers[observerKey] === 'undefined') {
480
- observers[observerKey] = createObserver(Observer, options);
481
- observersCache.set(Observer, observers);
482
- }
483
- return observers[observerKey];
484
- }
485
- function useObserver(Observer, opts = {}, initialEntry = {}) {
486
- const {
487
- root = null,
488
- rootMargin = null,
489
- threshold: defaultThreshold = null,
490
- disabled = false
491
- } = opts;
492
- const [entry, setEntry] = react.useState(initialEntry);
493
- const threshold = react.useMemo(() => defaultThreshold, [defaultThreshold]);
494
- const nodeRef = react.useRef(null);
495
- const currentElement = react.useRef(null);
496
- const elementChanged = nodeRef.current !== currentElement.current;
497
- react.useEffect(() => {
498
- if (disabled) {
499
- return () => {};
500
- }
501
- const {
502
- current: nodeElement
503
- } = nodeRef;
504
- const callback = newEntry => setEntry(newEntry);
505
- let unsubscribe = null;
506
- if (nodeElement !== null) {
507
- const newOpts = {};
508
- if (root !== null) {
509
- newOpts.root = root;
510
- }
511
- if (rootMargin !== null) {
512
- newOpts.rootMargin = rootMargin;
513
- }
514
- if (threshold !== null) {
515
- newOpts.threshold = threshold;
516
- }
517
- const {
518
- subscribe,
519
- unsubscribe: localUnsubscribe
520
- } = getObserver(Observer, newOpts);
521
- unsubscribe = localUnsubscribe;
522
- subscribe(nodeElement, callback);
523
- }
524
- currentElement.current = nodeElement;
525
- return () => {
526
- if (unsubscribe !== null) {
527
- unsubscribe(nodeElement, callback);
528
- }
529
- };
530
- }, [Observer, elementChanged, disabled, root, rootMargin, threshold]);
531
- return {
532
- ref: nodeRef,
533
- entry
534
- };
535
- }
536
-
537
- /**
538
- * Intersection Observer
539
- */
540
- const defaultThreshold = [0, 1.0];
541
- const intersectionObserverInitialEntry = {
542
- target: null,
543
- time: null,
544
- isVisible: false,
545
- isIntersecting: false,
546
- intersectionRatio: 0,
547
- intersectionRect: null,
548
- boundingClientRect: null,
549
- rootBounds: null
550
- };
551
- function useIntersectionObserver({
552
- root = null,
553
- rootMargin = '0px',
554
- threshold = defaultThreshold,
555
- disabled = false
556
- } = {}) {
557
- return useObserver(typeof IntersectionObserver !== 'undefined' ? IntersectionObserver : null, {
558
- root,
559
- rootMargin,
560
- threshold,
561
- disabled
562
- }, intersectionObserverInitialEntry);
563
- }
564
-
565
- /**
566
- * Resize Observer
567
- */
568
- const resizeObserverInitialEntry = {
569
- target: null,
570
- contentRect: null,
571
- contentBoxSize: null,
572
- borderBoxSize: null
573
- };
574
- function useResizeObserver({
575
- disabled = false
576
- } = {}) {
577
- return useObserver(typeof ResizeObserver !== 'undefined' ? ResizeObserver : null, {
578
- disabled
579
- }, resizeObserverInitialEntry);
580
- }
581
-
582
- function useIsVisible({
583
- persist = false,
584
- ...opts
585
- } = {}) {
586
- const {
587
- ref,
588
- entry: {
589
- isIntersecting
590
- }
591
- } = useIntersectionObserver(opts);
592
- const wasIntersecting = react.useRef(isIntersecting);
593
- if (isIntersecting && !wasIntersecting.current) {
594
- wasIntersecting.current = isIntersecting;
595
- }
596
- const isVisible = !persist && isIntersecting || persist && wasIntersecting.current;
597
- return {
598
- ref: !persist || !isVisible ? ref : {
599
- current: null
600
- },
601
- visible: isVisible
602
- };
603
- }
604
-
605
- const eventsManager = typeof window !== 'undefined' ? new EventsManager(window) : null;
606
- function useWindowEvent(event, callback) {
607
- react.useEffect(() => {
608
- if (eventsManager !== null && callback !== null) {
609
- eventsManager.subscribe(event, callback);
610
- }
611
- return () => {
612
- if (eventsManager !== null && callback !== null) {
613
- eventsManager.unsubscribe(event, callback);
614
- }
615
- };
616
- }, [event, callback]);
617
- }
618
-
619
- function useKeyboard(keyMap = null) {
620
- const onKeyDown = react.useCallback(event => {
621
- const {
622
- key
623
- } = event;
624
- let callback = null;
625
- if (typeof keyMap === 'function') {
626
- callback = keyMap;
627
- } else if (typeof keyMap[key] !== 'undefined') {
628
- callback = typeof keyMap[key] === 'function' ? keyMap[key] : (keyMap[key] || {}).down || null;
629
- }
630
- if (callback !== null) {
631
- callback(event);
632
- }
633
- }, [keyMap]);
634
- const onKeyUp = react.useCallback(event => {
635
- const {
636
- key
637
- } = event;
638
- let callback = null;
639
- if (typeof keyMap === 'function') {
640
- callback = keyMap;
641
- } else if (typeof keyMap[key] !== 'undefined') {
642
- callback = typeof keyMap[key] === 'function' ? keyMap[key] : (keyMap[key] || {}).up || null;
643
- }
644
- if (callback !== null) {
645
- callback(event);
646
- }
647
- }, [keyMap]);
648
- useWindowEvent('keydown', keyMap !== null ? onKeyDown : null);
649
- useWindowEvent('keyup', keyMap !== null ? onKeyUp : null);
650
- }
651
-
652
- const useItemsPaginated = (loader, {
653
- page = null,
654
- count = 10,
655
- pages: initialPages = null,
656
- getPageFromResponse = ({
657
- pagination: {
658
- page: currentPage,
659
- last_page: lastPage,
660
- total
661
- },
662
- data: items
663
- }) => ({
664
- page: parseInt(currentPage, 10),
665
- lastPage: parseInt(lastPage, 10),
666
- total: parseInt(total, 10),
667
- items
668
- }),
669
- onLoaded = null,
670
- onError = null,
671
- query = null
672
- } = {}) => {
673
- const lastState = react.useRef(null);
674
- const initialState = react.useMemo(() => {
675
- const finalInitialPages = initialPages !== null ? initialPages.map(it => getPageFromResponse(it)) : null;
676
- const finalLastPage = finalInitialPages !== null ? finalInitialPages.reduce((currentLastPage, {
677
- lastPage: initialLastPage
678
- }) => initialLastPage > currentLastPage ? initialLastPage : currentLastPage, -1) : -1;
679
- return {
680
- lastPage: finalLastPage,
681
- total: finalInitialPages !== null ? finalInitialPages[0].total : 0,
682
- loaded: finalLastPage !== -1 && finalInitialPages !== null && finalInitialPages.length === finalLastPage,
683
- loading: false,
684
- pages: finalInitialPages !== null ? finalInitialPages : null
685
- };
686
- }, [initialPages]);
687
- const currentPagesRef = react.useRef(initialState.pages);
688
- const [state, setState] = react.useState(initialState);
689
- const {
690
- lastPage,
691
- loaded,
692
- loading,
693
- pages,
694
- total
695
- } = state;
696
- const items = pages !== null ? pages.reduce((pagesItems, {
697
- items: pageItems
698
- }) => pagesItems.concat(pageItems), []) : null;
699
- const updateState = update => setState({
700
- ...state,
701
- ...update
702
- });
703
- const updateFromResponse = (response, error = null, reset = false) => {
704
- if (error !== null) {
705
- updateState({
706
- loaded: false,
707
- loading: false
708
- });
709
- throw error;
710
- }
711
- const newPage = getPageFromResponse(response);
712
- const currentPages = currentPagesRef.current || [];
713
- const newPages = (reset ? [newPage] : [...currentPages.filter(it => it.page !== newPage.page), newPage]).sort((a, b) => {
714
- const {
715
- page: aPage = null
716
- } = a;
717
- const {
718
- page: bPage = null
719
- } = b;
720
- const hasObject = aPage !== null && bPage !== null;
721
- if (hasObject) {
722
- if (aPage === bPage) {
723
- return 0;
724
- }
725
- return aPage > bPage ? 1 : -1;
726
- }
727
- if (isNumber(a) && isNumber(b)) {
728
- if (a === b) {
729
- return 0;
730
- }
731
- return a > b ? 1 : -1;
732
- }
733
- return 0;
734
- });
735
- currentPagesRef.current = newPages;
736
- updateState({
737
- loaded: true,
738
- loading: false,
739
- lastPage: newPage.lastPage,
740
- total: newPage.total,
741
- pages: newPages
742
- });
743
- return newPage;
744
- };
745
- const getNextPage = () => {
746
- const allPages = lastPage !== -1 ? Array.call(null, ...Array(lastPage)).map((it, index) => index + 1) : [];
747
- const currentPages = currentPagesRef.current || [];
748
- const remainingPages = allPages.filter(pageNumber => currentPages.findIndex(it => it.page === pageNumber) === -1);
749
- const firstItem = remainingPages.length > 0 ? remainingPages.shift() : null;
750
- return firstItem !== null ? firstItem : null;
751
- };
752
- const loadItems = requestPage => {
753
- updateState({
754
- loading: true
755
- });
756
- return cancelablePromise.cancelable(loader(requestPage, count)).then(response => updateFromResponse(response)).catch(error => updateFromResponse(null, error)).then(response => {
757
- if (onLoaded !== null) {
758
- onLoaded(response);
759
- }
760
- return response;
761
- }).catch(error => {
762
- if (onError !== null) {
763
- onError(error);
764
- }
765
- });
766
- };
767
- const loadPage = pageToLoad => {
768
- if (loading) {
769
- return Promise.reject();
770
- }
771
- const currentPages = currentPagesRef.current || [];
772
- const foundPage = currentPages.find(it => it.page === pageToLoad) || null;
773
- if (foundPage !== null) {
774
- return Promise.resolve(foundPage);
775
- }
776
- return loadItems(pageToLoad);
777
- };
778
- const loadNextPage = () => {
779
- if (loading) {
780
- return Promise.reject();
781
- }
782
- const nextPage = getNextPage();
783
- return nextPage !== null ? loadItems(nextPage) : Promise.resolve();
784
- };
785
-
786
- // Reset all on query change
787
- react.useEffect(() => {
788
- const hadState = lastState.current !== null;
789
- if (hadState) {
790
- currentPagesRef.current = null;
791
- updateState({
792
- loaded: true,
793
- loading: false,
794
- lastPage: null,
795
- total: 0,
796
- pages: []
797
- });
798
- }
799
- }, [query]);
800
- react.useEffect(() => {
801
- const hadState = lastState.current !== null;
802
- lastState.current = initialState;
803
- if (hadState) {
804
- currentPagesRef.current = initialState.pages;
805
- setState(initialState);
806
- }
807
- }, [initialState]);
808
- react.useEffect(() => {
809
- if (loader === null) {
810
- return () => {};
811
- }
812
- let loadPromise = null;
813
- const pageToLoad = initialPages === null && page === null ? 1 : page;
814
- if (pageToLoad !== null) {
815
- loadPromise = loadItems(pageToLoad);
816
- }
817
- return () => {
818
- if (loadPromise !== null) {
819
- loadPromise.cancel();
820
- }
821
- };
822
- }, [loader, page]);
823
- const currentPage = pages !== null ? pages.find(({
824
- page: pageNumber
825
- }) => parseInt(pageNumber, 10) === parseInt(page, 10)) || null : null;
826
- return {
827
- items,
828
- pages,
829
- currentPage,
830
- pageItems: currentPage !== null ? currentPage.items : null,
831
- total,
832
- lastPage,
833
- loaded,
834
- allLoaded: lastPage !== -1 && pages.length === lastPage,
835
- loading,
836
- loadNextPage,
837
- loadPage
838
- };
839
- };
840
-
841
- function usePlayerCurrentTime(player, {
842
- id = null,
843
- disabled = false,
844
- updateInterval = 1000,
845
- onUpdate: customOnUpdate = null,
846
- getCurrentTime = p => p.currentTime
847
- } = {}) {
848
- const [currentTime, setCurrentTime] = react.useState(0);
849
- const realCurrentTime = react.useRef(currentTime);
850
- const lastIdRef = react.useRef(id);
851
- const idChanged = lastIdRef.current !== id;
852
- if (idChanged) {
853
- realCurrentTime.current = 0;
854
- lastIdRef.current = id;
855
- }
856
-
857
- // Check time update
858
- react.useEffect(() => {
859
- if (disabled || player === null) {
860
- return () => {};
861
- }
862
- let canceled = false;
863
- const updateTime = time => {
864
- if (canceled) {
865
- return;
866
- }
867
- realCurrentTime.current = time;
868
- setCurrentTime(time);
869
- if (customOnUpdate !== null) {
870
- customOnUpdate(time);
871
- }
872
- };
873
- const interval = setInterval(() => {
874
- const time = getCurrentTime(player);
875
- if (typeof time.then !== 'undefined') {
876
- time.then(updateTime);
877
- } else {
878
- updateTime(time);
879
- }
880
- }, updateInterval);
881
- return () => {
882
- canceled = true;
883
- clearInterval(interval);
884
- };
885
- }, [id, player, setCurrentTime, disabled, updateInterval, getCurrentTime]);
886
- return realCurrentTime.current;
887
- }
888
-
889
- const NO_PLAYER_ERROR$2 = new Error('No player');
890
- function useNativeVideoPlayer(url, {
891
- width = 0,
892
- height = 0,
893
- duration = 0,
894
- muted: providedMuted = false,
895
- initialMuted = false,
896
- timeUpdateInterval = 1000,
897
- onTimeUpdate: customOnTimeUpdate = null
898
- } = {}) {
899
- const debug = react.useMemo(() => createDebug('folklore:video:native'), []);
900
- const elementRef = react.useRef(null);
901
- const [loaded, setLoaded] = react.useState(false);
902
- const [muted, setMuted] = react.useState(initialMuted || providedMuted);
903
- const [playState, setPlayState] = react.useState({
904
- playing: false,
905
- paused: false,
906
- ended: false,
907
- buffering: false
908
- });
909
- const [metadata, setMetadata] = react.useState({
910
- width,
911
- height,
912
- duration
913
- });
914
- const play = react.useCallback(() => {
915
- const {
916
- current: player
917
- } = elementRef;
918
- return player !== null ? player.play() : Promise.reject(NO_PLAYER_ERROR$2);
919
- }, []);
920
- const pause = react.useCallback(() => {
921
- const {
922
- current: player
923
- } = elementRef;
924
- return player !== null ? player.pause() : Promise.reject(NO_PLAYER_ERROR$2);
925
- }, []);
926
- const setVolume = react.useCallback(newVolume => {
927
- const {
928
- current: player
929
- } = elementRef;
930
- if (player !== null) {
931
- player.volume = newVolume;
932
- return Promise.resolve(newVolume);
933
- }
934
- return Promise.reject(NO_PLAYER_ERROR$2);
935
- }, []);
936
- const mute = react.useCallback(() => {
937
- const {
938
- current: player
939
- } = elementRef;
940
- if (player !== null) {
941
- player.muted = true;
942
- return Promise.resolve(true);
943
- }
944
- return Promise.reject(NO_PLAYER_ERROR$2);
945
- }, []);
946
- const unmute = react.useCallback(() => {
947
- const {
948
- current: player
949
- } = elementRef;
950
- if (player !== null) {
951
- player.muted = false;
952
- return Promise.resolve(false);
953
- }
954
- return Promise.reject(NO_PLAYER_ERROR$2);
955
- }, []);
956
- const seek = react.useCallback(newTime => {
957
- const {
958
- current: player
959
- } = elementRef;
960
- if (player !== null) {
961
- player.currentTime = newTime;
962
- return Promise.resolve(newTime);
963
- }
964
- return Promise.reject(NO_PLAYER_ERROR$2);
965
- }, []);
966
- const setLoop = react.useCallback(newLoop => {
967
- const {
968
- current: player
969
- } = elementRef;
970
- if (player !== null) {
971
- player.loop = newLoop;
972
- return Promise.resolve(newLoop);
973
- }
974
- return Promise.reject(NO_PLAYER_ERROR$2);
975
- }, []);
976
-
977
- // Bind player events
978
- react.useEffect(() => {
979
- const {
980
- current: player
981
- } = elementRef;
982
- if (player === null) {
983
- return () => {};
984
- }
985
- const onCanPlay = () => {
986
- setLoaded(true);
987
- const newMetadata = {
988
- duration: player.duration,
989
- width: player.videoWidth,
990
- height: player.videoHeight
991
- };
992
- setMetadata(newMetadata);
993
- debug('onCanPlay [URL: %s]', url);
994
- };
995
- const onMetadataLoaded = () => {
996
- const newMetadata = {
997
- duration: player.duration,
998
- width: player.videoWidth,
999
- height: player.videoHeight
1000
- };
1001
- setMetadata(newMetadata);
1002
- debug('onMetadataLoaded [URL: %s]', url);
1003
- };
1004
- const onPlay = () => {
1005
- setPlayState({
1006
- playing: true,
1007
- paused: false,
1008
- ended: false,
1009
- buffering: false
1010
- });
1011
- debug('onPlay [URL: %s]', url);
1012
- };
1013
- const onPause = () => {
1014
- setPlayState({
1015
- playing: false,
1016
- paused: true,
1017
- ended: false,
1018
- buffering: false
1019
- });
1020
- debug('onPause [URL: %s]', url);
1021
- };
1022
- const onVolumeChange = () => {
1023
- setMuted(player.muted);
1024
- debug('onVolumeChange [URL: %s]', url);
1025
- };
1026
- const onEnded = () => {
1027
- setPlayState({
1028
- playing: false,
1029
- paused: false,
1030
- ended: true,
1031
- buffering: false
1032
- });
1033
- debug('onEnded [URL: %s]', url);
1034
- };
1035
- debug('Bind events [URL: %s]', url);
1036
- player.addEventListener('canplay', onCanPlay);
1037
- player.addEventListener('metadataloaded', onMetadataLoaded);
1038
- player.addEventListener('play', onPlay);
1039
- player.addEventListener('volumechange', onVolumeChange);
1040
- player.addEventListener('pause', onPause);
1041
- player.addEventListener('ended', onEnded);
1042
- return () => {
1043
- debug('Unbind events [URL: %s]', url);
1044
- player.removeEventListener('canplay', onCanPlay);
1045
- player.removeEventListener('metadataloaded', onMetadataLoaded);
1046
- player.removeEventListener('play', onPlay);
1047
- player.removeEventListener('volumechange', onVolumeChange);
1048
- player.removeEventListener('pause', onPause);
1049
- player.removeEventListener('ended', onEnded);
1050
- };
1051
- }, [url, setPlayState, setMetadata, setMuted, setLoaded]);
1052
- const {
1053
- playing
1054
- } = playState;
1055
- const currentTime = usePlayerCurrentTime(elementRef.current, {
1056
- id: url,
1057
- disabled: !playing || timeUpdateInterval === null,
1058
- updateInterval: timeUpdateInterval,
1059
- onUpdate: customOnTimeUpdate
1060
- });
1061
- return {
1062
- ref: elementRef,
1063
- player: elementRef.current,
1064
- play,
1065
- pause,
1066
- mute,
1067
- unmute,
1068
- setVolume,
1069
- seek,
1070
- setLoop,
1071
- ready: true,
1072
- currentTime,
1073
- loaded,
1074
- muted,
1075
- ...metadata,
1076
- ...playState
1077
- };
1078
- }
1079
-
1080
- const getWindowSize = () => ({
1081
- width: typeof window !== 'undefined' ? window.innerWidth || 0 : 0,
1082
- height: typeof window !== 'undefined' ? window.innerHeight || 0 : 0
1083
- });
1084
- let currentSize = null;
1085
- function useWindowSize({
1086
- onChange = null,
1087
- onMount = false,
1088
- memo = false
1089
- } = {}) {
1090
- const [size, setSize] = react.useState(() => onMount ? getWindowSize() : {
1091
- width: 0,
1092
- height: 0
1093
- });
1094
- const sizeRef = react.useRef(size);
1095
- if (currentSize === null && memo) {
1096
- currentSize = size;
1097
- }
1098
- const updateSize = react.useCallback(() => {
1099
- const newSize = getWindowSize();
1100
- if (memo && (currentSize.width !== newSize.width || currentSize.height !== newSize.height)) {
1101
- currentSize = newSize;
1102
- }
1103
- if (sizeRef.current.width !== newSize.width || sizeRef.current.height !== newSize.height) {
1104
- sizeRef.current = newSize;
1105
- setSize(newSize);
1106
- return newSize;
1107
- }
1108
- return null;
1109
- }, [setSize, memo]);
1110
- const onResize = react.useCallback(() => {
1111
- const newSize = updateSize();
1112
- if (newSize !== null && onChange !== null) {
1113
- onChange(newSize);
1114
- }
1115
- }, [onChange, updateSize]);
1116
- useWindowEvent('resize', onResize);
1117
- react.useEffect(() => {
1118
- onResize();
1119
- }, []);
1120
- return size;
1121
- }
1122
-
1123
- function useScrollTrigger({
1124
- disabled = false,
1125
- triggers = [0.1, 0.25, 0.5, 0.75, 0.9, 1.0],
1126
- useElementScroll = false,
1127
- onTrigger = null
1128
- } = {}) {
1129
- const triggersCompletedRef = react.useRef([]);
1130
- const {
1131
- height
1132
- } = useWindowSize();
1133
- const {
1134
- ref,
1135
- entry: {
1136
- contentRect = null
1137
- }
1138
- } = useResizeObserver({
1139
- disabled: !useElementScroll
1140
- });
1141
- const {
1142
- top: elementTop = 0,
1143
- height: elementHeight = null
1144
- } = contentRect || {};
1145
- const elementScrollHeight = elementHeight !== null ? elementHeight + elementTop : 0;
1146
- react.useEffect(() => {
1147
- if (eventsManager === null || disabled) {
1148
- return () => {};
1149
- }
1150
- function onScroll() {
1151
- const scrollY = useElementScroll ? ref.current.scrollTop : window.scrollY;
1152
- let scrollHeight = 0;
1153
- if (useElementScroll) {
1154
- scrollHeight = ref.current.scrollHeight - ref.current.clientHeight;
1155
- } else if (ref.current !== null && elementScrollHeight > 0) {
1156
- scrollHeight = elementScrollHeight - height;
1157
- } else {
1158
- scrollHeight = document.documentElement.scrollHeight - height;
1159
- }
1160
- const progress = Math.min(Math.max(scrollY / scrollHeight, 0), 1);
1161
- const newTriggersCompleted = triggers.filter(step => progress >= step && triggersCompletedRef.current.indexOf(step) === -1);
1162
- newTriggersCompleted.forEach(step => {
1163
- if (onTrigger != null) {
1164
- onTrigger(step);
1165
- }
1166
- });
1167
- if (newTriggersCompleted.length > 0) {
1168
- triggersCompletedRef.current = [...triggersCompletedRef.current, ...newTriggersCompleted];
1169
- }
1170
- }
1171
- if (useElementScroll) {
1172
- ref.current.addEventListener('scroll', onScroll);
1173
- } else {
1174
- eventsManager.subscribe('scroll', onScroll);
1175
- }
1176
- onScroll();
1177
- return () => {
1178
- if (useElementScroll) {
1179
- ref.current.removeEventListener('scroll', onScroll);
1180
- } else {
1181
- eventsManager.unsubscribe('scroll', onScroll);
1182
- }
1183
- };
1184
- }, [triggers, disabled, onTrigger, height, elementScrollHeight, useElementScroll]);
1185
- return {
1186
- ref
1187
- };
1188
- }
1189
-
1190
- function checkWebpSupport() {
1191
- return new Promise(resolve => {
1192
- const img = document.createElement('img');
1193
- img.onload = () => {
1194
- resolve(img.width > 0 && img.height > 0);
1195
- };
1196
- img.onerror = () => {
1197
- resolve(false);
1198
- };
1199
- img.src = 'data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoCAAEAAQAcJaQAA3AA/v3AgAA=';
1200
- });
1201
- }
1202
- function useSupportsWebp(defaultValue = true) {
1203
- const [supportsWebp, setSupportsWebp] = react.useState(defaultValue);
1204
- react.useEffect(() => {
1205
- let canceled = false;
1206
- checkWebpSupport().then(newSupport => {
1207
- if (!canceled && newSupport !== supportsWebp) {
1208
- setSupportsWebp(newSupport);
1209
- }
1210
- });
1211
- return () => {
1212
- canceled = true;
1213
- };
1214
- }, []);
1215
- return supportsWebp;
1216
- }
1217
-
1218
- const NO_PLAYER_ERROR$1 = new Error('No player');
1219
- function useVimeoPlayer(id, {
1220
- width = 0,
1221
- height = 0,
1222
- duration = 0,
1223
- autoplay = false,
1224
- autopause = true,
1225
- byline = false,
1226
- controls = false,
1227
- muted: providedMuted = false,
1228
- initialMuted = false,
1229
- timeUpdateInterval = 1000,
1230
- onTimeUpdate: customOnTimeUpdate = null,
1231
- getVideoId = url => {
1232
- if (url === null || url.match(/^[0-9]+$/) !== null) {
1233
- return url;
1234
- }
1235
- const match = url.match(/\/[0-9]+/);
1236
- return match !== null ? match[1] : null;
1237
- }
1238
- } = {}) {
1239
- const debug = react.useMemo(() => createDebug('folklore:video:vimeo'), []);
1240
- const [apiLoaded, setApiLoaded] = react.useState(false);
1241
- const apiRef = react.useRef(null);
1242
- const elementRef = react.useRef(null);
1243
- const playerRef = react.useRef(null);
1244
- const playerElementRef = react.useRef(elementRef.current);
1245
- const elementHasChanged = elementRef.current !== playerElementRef.current;
1246
- const videoId = react.useMemo(() => getVideoId(id), [id]);
1247
- const [ready, setReady] = react.useState(false);
1248
- const [loaded, setLoaded] = react.useState(false);
1249
- const [volume, setVolumeState] = react.useState(initialMuted || providedMuted ? 0 : 1);
1250
- const [playState, setPlayState] = react.useState({
1251
- playing: false,
1252
- paused: false,
1253
- ended: false,
1254
- buffering: false
1255
- });
1256
- const [metadata, setMetadata] = react.useState({
1257
- width,
1258
- height,
1259
- duration
1260
- });
1261
-
1262
- // Load SDK
1263
- react.useEffect(() => {
1264
- let canceled = false;
1265
- if (!apiLoaded && id !== null) {
1266
- debug('Load API');
1267
- services.loadVimeo().then(api => {
1268
- if (!canceled) {
1269
- apiRef.current = api;
1270
- setApiLoaded(true);
1271
- debug('API Loaded');
1272
- }
1273
- });
1274
- }
1275
- return () => {
1276
- canceled = true;
1277
- };
1278
- }, [id]);
1279
- const play = react.useCallback(() => {
1280
- const {
1281
- current: player
1282
- } = playerRef;
1283
- return player !== null ? player.play() : Promise.reject(NO_PLAYER_ERROR$1);
1284
- }, []);
1285
- const pause = react.useCallback(() => {
1286
- const {
1287
- current: player
1288
- } = playerRef;
1289
- return player !== null ? player.pause() : Promise.reject(NO_PLAYER_ERROR$1);
1290
- }, []);
1291
- const setVolume = react.useCallback(newVolume => {
1292
- const {
1293
- current: player
1294
- } = playerRef;
1295
- return player !== null ? player.setVolume(newVolume) : Promise.reject(NO_PLAYER_ERROR$1);
1296
- }, []);
1297
- const mute = react.useCallback(() => {
1298
- const {
1299
- current: player
1300
- } = playerRef;
1301
- return player !== null ? player.setVolume(0) : Promise.reject(NO_PLAYER_ERROR$1);
1302
- }, []);
1303
- const unmute = react.useCallback(() => {
1304
- const {
1305
- current: player
1306
- } = playerRef;
1307
- return player !== null ? player.setVolume(1) : Promise.reject(NO_PLAYER_ERROR$1);
1308
- }, []);
1309
- const seek = react.useCallback(time => {
1310
- const {
1311
- current: player
1312
- } = playerRef;
1313
- return player !== null ? player.setCurrentTime(time) : Promise.reject(NO_PLAYER_ERROR$1);
1314
- }, []);
1315
- const setLoop = react.useCallback(loop => {
1316
- const {
1317
- current: player
1318
- } = playerRef;
1319
- return player !== null ? player.setLoop(loop) : Promise.reject(NO_PLAYER_ERROR$1);
1320
- }, []);
1321
- const destroyVideo = react.useCallback(() => {
1322
- const {
1323
- current: player
1324
- } = playerRef;
1325
- if (player !== null) {
1326
- debug('Unload video');
1327
- player.unload();
1328
- }
1329
- if (player !== null) {
1330
- debug('Unset video');
1331
- playerRef.current = null;
1332
- }
1333
- }, []);
1334
- react.useEffect(() => {
1335
- const {
1336
- current: currentPlayer
1337
- } = playerRef;
1338
- if (playerElementRef.current !== elementRef.current && currentPlayer !== null) {
1339
- debug('iFrame switched');
1340
- destroyVideo();
1341
- }
1342
- }, [elementHasChanged]);
1343
-
1344
- // Create player
1345
- react.useEffect(() => {
1346
- const {
1347
- current: Player = null
1348
- } = apiRef;
1349
- const {
1350
- current: currentPlayer = null
1351
- } = playerRef;
1352
- const {
1353
- current: element = null
1354
- } = elementRef;
1355
- if (!apiLoaded || videoId === null || element === null) {
1356
- destroyVideo();
1357
- return;
1358
- }
1359
- let player = currentPlayer;
1360
- if (player === null) {
1361
- debug('Create player [ID: %s]', videoId);
1362
- player = new Player(element, {
1363
- id: videoId,
1364
- autoplay,
1365
- autopause,
1366
- byline,
1367
- controls,
1368
- muted: initialMuted,
1369
- background: !controls
1370
- });
1371
- player.ready().then(() => setReady(true)).catch(e => {
1372
- debug('ERROR: %o', e);
1373
- });
1374
- } else {
1375
- debug('Load video [ID: %s]', videoId);
1376
- player.loadVideo(videoId).catch(e => {
1377
- debug('ERROR: %o', e);
1378
- });
1379
- }
1380
- playerRef.current = player;
1381
- playerElementRef.current = element;
1382
- }, [apiLoaded, videoId, elementHasChanged, setReady, destroyVideo, setLoaded]);
1383
-
1384
- // Bind player events
1385
- react.useEffect(() => {
1386
- const {
1387
- current: player
1388
- } = playerRef;
1389
- if (player === null) {
1390
- return () => {};
1391
- }
1392
- let canceled = false;
1393
- const onLoaded = () => {
1394
- Promise.all([player.getDuration(), player.getVideoWidth(), player.getVideoHeight(), player.getMuted()]).then(([newDuration, newWidth, newHeight, newMuted]) => {
1395
- if (canceled) {
1396
- return;
1397
- }
1398
- const newMetadata = {
1399
- duration: newDuration,
1400
- width: newWidth,
1401
- height: newHeight
1402
- };
1403
- setMetadata(newMetadata);
1404
- if (newMuted) {
1405
- setVolumeState(0);
1406
- }
1407
- setLoaded(true);
1408
- });
1409
- debug('onLoaded [ID: %s]', videoId);
1410
- };
1411
- const onPlay = () => {
1412
- setPlayState({
1413
- playing: true,
1414
- paused: false,
1415
- ended: false,
1416
- buffering: false
1417
- });
1418
- debug('onPlay [ID: %s]', videoId);
1419
- player.getMuted().then(newMuted => {
1420
- if (!canceled && newMuted) {
1421
- setVolumeState(0);
1422
- }
1423
- }).catch(() => {});
1424
- };
1425
- const onPause = () => {
1426
- setPlayState({
1427
- playing: false,
1428
- paused: true,
1429
- ended: false,
1430
- buffering: false
1431
- });
1432
- debug('onPause [ID: %s]', videoId);
1433
- };
1434
- const onVolumeChange = ({
1435
- volume: newVolume
1436
- }) => {
1437
- setVolumeState(newVolume);
1438
- debug('onVolumeChange [ID: %s]', videoId);
1439
- };
1440
- const onEnded = () => {
1441
- setPlayState({
1442
- playing: false,
1443
- paused: false,
1444
- ended: true,
1445
- buffering: false
1446
- });
1447
- debug('onEnded [ID: %s]', videoId);
1448
- };
1449
- const onBufferStart = () => {
1450
- setPlayState({
1451
- playing: true,
1452
- paused: false,
1453
- ended: false,
1454
- buffering: true
1455
- });
1456
- debug('onBufferStart [ID: %s]', videoId);
1457
- };
1458
- const onBufferEnded = () => {
1459
- setPlayState({
1460
- playing: true,
1461
- paused: false,
1462
- ended: false,
1463
- buffering: false
1464
- });
1465
- debug('onBufferStart [ID: %s]', videoId);
1466
- };
1467
- debug('Bind events [ID: %s]', videoId);
1468
- player.on('loaded', onLoaded);
1469
- player.on('play', onPlay);
1470
- player.on('pause', onPause);
1471
- player.on('volumechange', onVolumeChange);
1472
- player.on('ended', onEnded);
1473
- player.on('bufferstart', onBufferStart);
1474
- player.on('bufferend', onBufferEnded);
1475
- return () => {
1476
- canceled = true;
1477
- debug('Unbind events [ID: %s]', videoId);
1478
- player.off('loaded', onLoaded);
1479
- player.off('play', onPlay);
1480
- player.off('pause', onPause);
1481
- player.off('volumechange', onVolumeChange);
1482
- player.off('ended', onEnded);
1483
- player.off('bufferstart', onBufferStart);
1484
- player.off('bufferend', onBufferEnded);
1485
- };
1486
- }, [videoId, playerRef.current, setPlayState, setMetadata, setVolumeState, setLoaded]);
1487
- const {
1488
- playing
1489
- } = playState;
1490
- const getCurrentTime = react.useCallback(p => p.getCurrentTime(), []);
1491
- const currentTime = usePlayerCurrentTime(playerRef.current, {
1492
- id: videoId,
1493
- disabled: !playing || timeUpdateInterval === null,
1494
- updateInterval: timeUpdateInterval,
1495
- onUpdate: customOnTimeUpdate,
1496
- getCurrentTime
1497
- });
1498
- return {
1499
- ref: elementRef,
1500
- player: playerRef.current,
1501
- play,
1502
- pause,
1503
- mute,
1504
- unmute,
1505
- setVolume,
1506
- seek,
1507
- setLoop,
1508
- ready,
1509
- currentTime,
1510
- loaded,
1511
- muted: volume === 0,
1512
- volume,
1513
- ...metadata,
1514
- ...playState
1515
- };
1516
- }
1517
-
1518
- const NO_PLAYER_ERROR = new Error('No player');
1519
- function useYouTubePlayer(id, {
1520
- width = 0,
1521
- height = 0,
1522
- duration = 0,
1523
- autoplay = false,
1524
- controls = true,
1525
- timeUpdateInterval = 1000,
1526
- muted: providedMuted = false,
1527
- initialMuted = false,
1528
- onVolumeChange: customOnVolumeChange = null,
1529
- onTimeUpdate: customOnTimeUpdate = null,
1530
- getVideoId = url => {
1531
- if (url === null || url.match(/^https?:/) === null) {
1532
- return url;
1533
- }
1534
- const regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&?]*).*/;
1535
- const match = url.match(regExp);
1536
- return match !== null ? match[7] : null;
1537
- }
1538
- } = {}) {
1539
- const debug = react.useMemo(() => createDebug('folklore:video:youtube'), []);
1540
- const [apiLoaded, setApiLoaded] = react.useState(typeof window !== 'undefined' && typeof window.YT !== 'undefined');
1541
- const apiRef = react.useRef(typeof window !== 'undefined' && typeof window.YT !== 'undefined' ? window.YT : null);
1542
- const elementRef = react.useRef(null);
1543
- const playerRef = react.useRef(null);
1544
- const playerElementRef = react.useRef(elementRef.current);
1545
- if (elementRef.current !== null && playerElementRef.current === null) {
1546
- playerElementRef.current = elementRef.current;
1547
- }
1548
- const elementHasChanged = elementRef.current !== playerElementRef.current;
1549
- const videoId = react.useMemo(() => getVideoId(id), [id]);
1550
- const [ready, setReady] = react.useState(false);
1551
- const [muted, setMuted] = react.useState(initialMuted || providedMuted);
1552
- const [playState, setPlayState] = react.useState({
1553
- playing: false,
1554
- paused: false,
1555
- ended: false,
1556
- buffering: false
1557
- });
1558
- const [metadata, setMetadata] = react.useState({
1559
- width,
1560
- height,
1561
- duration
1562
- });
1563
- react.useEffect(() => {
1564
- let canceled = false;
1565
- if (!apiLoaded && videoId !== null) {
1566
- debug('Load API');
1567
- services.loadYouTube().then(api => {
1568
- if (!canceled) {
1569
- apiRef.current = api;
1570
- setApiLoaded(true);
1571
- debug('API Loaded');
1572
- }
1573
- });
1574
- }
1575
- return () => {
1576
- canceled = true;
1577
- };
1578
- }, [apiLoaded, videoId, setApiLoaded]);
1579
- const play = react.useCallback(() => {
1580
- const {
1581
- current: player
1582
- } = playerRef;
1583
- return player !== null && typeof player.playVideo !== 'undefined' ? Promise.resolve(player.playVideo()) : Promise.reject(NO_PLAYER_ERROR);
1584
- }, []);
1585
- const pause = react.useCallback(() => {
1586
- const {
1587
- current: player
1588
- } = playerRef;
1589
- return player !== null && typeof player.pauseVideo !== 'undefined' ? Promise.resolve(player.pauseVideo()) : Promise.reject(NO_PLAYER_ERROR);
1590
- }, []);
1591
- const setVolume = react.useCallback(volume => {
1592
- const {
1593
- current: player
1594
- } = playerRef;
1595
- const promise = player !== null && typeof player.setVolume !== 'undefined' ? Promise.resolve(player.setVolume(volume * 100)) : Promise.reject(NO_PLAYER_ERROR);
1596
- if (customOnVolumeChange) {
1597
- customOnVolumeChange(volume);
1598
- }
1599
- return promise;
1600
- }, [customOnVolumeChange]);
1601
- const mute = react.useCallback(() => {
1602
- const {
1603
- current: player
1604
- } = playerRef;
1605
- return (player !== null && typeof player.mute !== 'undefined' ? Promise.resolve(player.mute()) : Promise.reject(NO_PLAYER_ERROR)).then(() => setMuted(true));
1606
- }, [setMuted]);
1607
- const unmute = react.useCallback(() => {
1608
- const {
1609
- current: player
1610
- } = playerRef;
1611
- return (player !== null && typeof player.unMute !== 'undefined' ? Promise.resolve(player.unMute()) : Promise.reject(NO_PLAYER_ERROR)).then(() => setMuted(false));
1612
- }, []);
1613
- const seek = react.useCallback(time => {
1614
- const {
1615
- current: player
1616
- } = playerRef;
1617
- return player !== null && typeof player.seekTo !== 'undefined' ? Promise.resolve(player.seekTo(time)) : Promise.reject(NO_PLAYER_ERROR);
1618
- }, []);
1619
- const setLoop = react.useCallback(loop => {
1620
- const {
1621
- current: player
1622
- } = playerRef;
1623
- return player !== null && typeof player.setLoop !== 'undefined' ? Promise.resolve(player.setLoop(loop)) : Promise.reject(NO_PLAYER_ERROR);
1624
- }, []);
1625
- const destroyPlayer = react.useCallback(() => {
1626
- if (playerRef.current !== null) {
1627
- debug('Unset player');
1628
- playerRef.current = null;
1629
- }
1630
- }, []);
1631
-
1632
- // Detect iframe switch and destroy player
1633
-
1634
- react.useEffect(() => {
1635
- const {
1636
- current: currentPlayer
1637
- } = playerRef;
1638
- if (playerElementRef.current !== elementRef.current && currentPlayer !== null) {
1639
- debug('iFrame switched');
1640
- destroyPlayer();
1641
- }
1642
- }, [elementHasChanged]);
1643
-
1644
- // Create player
1645
- react.useEffect(() => {
1646
- const {
1647
- current: YT = null
1648
- } = apiRef;
1649
- const {
1650
- current: currentPlayer = null
1651
- } = playerRef;
1652
- const {
1653
- current: element = null
1654
- } = elementRef;
1655
- if (!apiLoaded || videoId === null || element === null) {
1656
- destroyPlayer();
1657
- return;
1658
- }
1659
- let player = currentPlayer;
1660
- if (player !== null && typeof player.loadVideoById !== 'undefined') {
1661
- debug('Switch video [ID: %s]', videoId);
1662
- player.loadVideoById(videoId);
1663
- } else {
1664
- debug('Create player [ID: %s]', videoId);
1665
- const onReady = ({
1666
- target
1667
- }) => {
1668
- player = target;
1669
- playerRef.current = target;
1670
- setReady(true);
1671
- const newDuration = target.getDuration();
1672
- if (newDuration !== metadata.duration) {
1673
- setMetadata({
1674
- ...metadata,
1675
- duration: newDuration
1676
- });
1677
- }
1678
- debug('onReady [ID: %s]', videoId);
1679
- if (muted) {
1680
- player.mute();
1681
- }
1682
- };
1683
- const onStateChange = ({
1684
- data: state
1685
- }) => {
1686
- const newState = {
1687
- playing: state === YT.PlayerState.PLAYING,
1688
- paused: state === YT.PlayerState.PAUSED,
1689
- ended: state === YT.PlayerState.ENDED,
1690
- buffering: state === YT.PlayerState.BUFFERING
1691
- };
1692
- setPlayState(newState);
1693
- let stateLabel = null;
1694
- if (newState.playing) {
1695
- stateLabel = 'playing';
1696
- } else if (newState.paused) {
1697
- stateLabel = 'paused';
1698
- } else if (newState.ended) {
1699
- stateLabel = 'ended';
1700
- } else if (newState.buffering) {
1701
- stateLabel = 'buffering';
1702
- } else if (state === -1) {
1703
- stateLabel = 'not started';
1704
- } else if (state === 0) {
1705
- stateLabel = 'stopped';
1706
- }
1707
- debug('onStateChange %s [ID: %s]', stateLabel, videoId);
1708
- };
1709
- player = new YT.Player(element, {
1710
- videoId,
1711
- playerVars: {
1712
- controls,
1713
- autoplay: autoplay ? 1 : 0,
1714
- mute: initialMuted,
1715
- playsinline: true,
1716
- rel: 0,
1717
- showinfo: 0,
1718
- modestbranding: 1
1719
- },
1720
- events: {
1721
- onReady,
1722
- onStateChange
1723
- }
1724
- });
1725
- }
1726
- playerRef.current = player;
1727
- playerElementRef.current = element;
1728
- }, [apiLoaded, videoId, elementHasChanged, setPlayState, setReady, setMetadata, destroyPlayer]);
1729
- const {
1730
- playing
1731
- } = playState;
1732
- const getCurrentTime = react.useCallback(p => p.getCurrentTime(), []);
1733
- const currentTime = usePlayerCurrentTime(playerRef.current, {
1734
- id: videoId,
1735
- disabled: !playing || timeUpdateInterval === null,
1736
- updateInterval: timeUpdateInterval,
1737
- onUpdate: customOnTimeUpdate,
1738
- getCurrentTime
1739
- });
1740
- return {
1741
- ref: elementRef,
1742
- player: playerRef.current,
1743
- play,
1744
- pause,
1745
- mute,
1746
- unmute,
1747
- setVolume,
1748
- seek,
1749
- setLoop,
1750
- ready,
1751
- currentTime,
1752
- muted,
1753
- loaded: ready,
1754
- ...metadata,
1755
- ...playState
1756
- };
1757
- }
1758
-
1759
- function useVideoPlayer(params) {
1760
- const {
1761
- service = null,
1762
- videoId = null,
1763
- url = null,
1764
- onLoaded: customOnLoaded = null,
1765
- onPlay: customOnPlay = null,
1766
- onPause: customOnPause = null,
1767
- onEnd: customOnEnd = null,
1768
- onMetadataChange: customOnMetadataChange = null,
1769
- onBufferStart: customOnBufferStart = null,
1770
- onBufferEnded: customOnBufferEnded = null
1771
- } = params || {};
1772
- const dailymotionPlayer = useDailymotionPlayer(service === 'dailymotion' ? videoId || url : null, params);
1773
- const youtubePlayer = useYouTubePlayer(service === 'youtube' ? videoId || url : null, params);
1774
- const vimeoPlayer = useVimeoPlayer(service === 'vimeo' ? videoId || url : null, params);
1775
- const nativePlayer = useNativeVideoPlayer(url, params);
1776
- let player = null;
1777
- if (service === 'dailymotion') {
1778
- player = dailymotionPlayer;
1779
- } else if (service === 'youtube') {
1780
- player = youtubePlayer;
1781
- } else if (service === 'vimeo') {
1782
- player = vimeoPlayer;
1783
- } else {
1784
- player = nativePlayer;
1785
- }
1786
- const {
1787
- playing = false,
1788
- paused = false,
1789
- buffering = false,
1790
- ended = false,
1791
- ready = false,
1792
- width: metaWidth = null,
1793
- height: metaHeight = null,
1794
- duration: metaDuration = null
1795
- } = player || {};
1796
- react.useEffect(() => {
1797
- if (ready && customOnLoaded !== null) {
1798
- customOnLoaded();
1799
- }
1800
- }, [ready, customOnLoaded]);
1801
- react.useEffect(() => {
1802
- if (playing && customOnPlay !== null) {
1803
- customOnPlay();
1804
- }
1805
- }, [playing /* , customOnPlay */]);
1806
- react.useEffect(() => {
1807
- if (paused && customOnPause !== null) {
1808
- customOnPause();
1809
- }
1810
- }, [paused /* , customOnPause */]);
1811
- react.useEffect(() => {
1812
- if (buffering && customOnBufferStart !== null) {
1813
- customOnBufferStart();
1814
- } else if (!buffering && customOnBufferEnded !== null) {
1815
- customOnBufferEnded();
1816
- }
1817
- }, [buffering /* , customOnBufferStart, customOnBufferEnded */]);
1818
- react.useEffect(() => {
1819
- if (ended && customOnEnd !== null) {
1820
- customOnEnd();
1821
- }
1822
- }, [ended /* , customOnEnd */]);
1823
- react.useEffect(() => {
1824
- const hasMetadata = metaWidth !== null || metaHeight !== null || metaDuration !== null;
1825
- if (hasMetadata && customOnMetadataChange !== null) {
1826
- customOnMetadataChange({
1827
- width: metaWidth,
1828
- height: metaHeight,
1829
- duration: metaDuration
1830
- });
1831
- }
1832
- }, [metaWidth, metaHeight, metaDuration, customOnMetadataChange]);
1833
- return player;
1834
- }
1835
-
1836
- function useVisualViewport() {
1837
- const {
1838
- width: windowWidth,
1839
- height: windowHeight
1840
- } = useWindowSize();
1841
- const [{
1842
- width: viewportWidth,
1843
- height: viewportHeight,
1844
- ...viewport
1845
- }, setViewport] = react.useState({
1846
- width: windowWidth,
1847
- height: windowHeight
1848
- });
1849
- const updateViewport = react.useCallback((viewPort = null) => {
1850
- const {
1851
- width: newWidth = 0,
1852
- height: newHeight = 0,
1853
- offsetTop: newOffsetTop = 0,
1854
- offsetLeft: newOffsetLeft = 0,
1855
- pageLeft: newPageLeft = 0,
1856
- pageTop: newPageTop = 0
1857
- } = viewPort || window.visualViewport || {};
1858
- setViewport({
1859
- width: newWidth,
1860
- height: newHeight,
1861
- offsetTop: newOffsetTop,
1862
- offsetLeft: newOffsetLeft,
1863
- pageLeft: newPageLeft,
1864
- pageTop: newPageTop
1865
- });
1866
- }, [setViewport]);
1867
- react.useEffect(() => {
1868
- if (typeof window.visualViewport === 'undefined') {
1869
- return () => {};
1870
- }
1871
- const onUpdate = e => {
1872
- updateViewport(e.target);
1873
- };
1874
- updateViewport();
1875
- window.visualViewport.addEventListener('resize', onUpdate);
1876
- window.visualViewport.addEventListener('scroll', onUpdate);
1877
- return () => {
1878
- window.visualViewport.removeEventListener('resize', onUpdate);
1879
- window.visualViewport.removeEventListener('scroll', onUpdate);
1880
- };
1881
- }, [updateViewport]);
1882
- return {
1883
- width: viewportWidth || windowWidth,
1884
- height: viewportHeight || windowHeight,
1885
- ...viewport,
1886
- updateViewport
1887
- };
1888
- }
1889
-
1890
- const getWindowScroll = () => ({
1891
- x: typeof window !== 'undefined' ? window.scrollX || 0 : 0,
1892
- y: typeof window !== 'undefined' ? window.scrollY || 0 : 0
1893
- });
1894
- let currentScroll = null;
1895
- function useWindowScroll({
1896
- onChange = null,
1897
- onMount = false,
1898
- memo = false
1899
- } = {}) {
1900
- const [scroll, setScroll] = react.useState(() => onMount ? getWindowScroll() : {
1901
- x: 0,
1902
- y: 0
1903
- });
1904
- const scrollRef = react.useRef(scroll);
1905
- if (currentScroll === null && memo) {
1906
- currentScroll = scroll;
1907
- }
1908
- const updateScroll = react.useCallback(() => {
1909
- const newScroll = getWindowScroll();
1910
- const {
1911
- x: currentX,
1912
- y: currentY
1913
- } = currentScroll || {};
1914
- if (memo && (currentX !== newScroll.x || currentY !== newScroll.y)) {
1915
- currentScroll = newScroll;
1916
- }
1917
- if (scrollRef.current.x !== newScroll.x || scrollRef.current.y !== newScroll.y) {
1918
- scrollRef.current = newScroll;
1919
- setScroll(newScroll);
1920
- return newScroll;
1921
- }
1922
- return null;
1923
- }, [setScroll, memo]);
1924
- const onScroll = react.useCallback(() => {
1925
- const newScroll = updateScroll();
1926
- if (newScroll !== null && onChange !== null) {
1927
- onChange(newScroll);
1928
- }
1929
- }, [updateScroll, onChange]);
1930
- useWindowEvent('scroll', onScroll);
1931
- react.useEffect(() => {
1932
- onScroll();
1933
- }, []);
1934
- return scroll;
1935
- }
1936
-
1937
- exports.documentEventsManager = eventsManager$1;
1938
- exports.getObserver = getObserver;
1939
- exports.useCounter = useCounter;
1940
- exports.useDailymotionPlayer = useDailymotionPlayer;
1941
- exports.useDocumentEvent = useDocumentEvent;
1942
- exports.useIntersectionObserver = useIntersectionObserver;
1943
- exports.useIsVisible = useIsVisible;
1944
- exports.useItemsPaginated = useItemsPaginated;
1945
- exports.useKeyboard = useKeyboard;
1946
- exports.useNativeVideoPlayer = useNativeVideoPlayer;
1947
- exports.useObserver = useObserver;
1948
- exports.usePlayerCurrentTime = usePlayerCurrentTime;
1949
- exports.useResizeObserver = useResizeObserver;
1950
- exports.useScrollTrigger = useScrollTrigger;
1951
- exports.useSupportsWebp = useSupportsWebp;
1952
- exports.useVideoPlayer = useVideoPlayer;
1953
- exports.useVimeoPlayer = useVimeoPlayer;
1954
- exports.useVisualViewport = useVisualViewport;
1955
- exports.useVisualViewportSize = useVisualViewport;
1956
- exports.useWindowEvent = useWindowEvent;
1957
- exports.useWindowScroll = useWindowScroll;
1958
- exports.useWindowSize = useWindowSize;
1959
- exports.useYouTubePlayer = useYouTubePlayer;
1960
- exports.windowEventsManager = eventsManager;