Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 5x 5x 8x 8x 14x 14x 11x 11x 11x 3x 3x 3x 3x 8x 5x 5x 5x 5x 1x 5x 5x 5x 5x | /**
* Copyright (c) 2024-present, Matti Bar-Zeev.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import React, {Children, ReactElement, ReactNode, RefObject, cloneElement, useEffect, useRef} from 'react';
import {ImageProcessor} from './image';
import {MediaHTMLElement, MediaProcessor} from './types';
import {VideoProcessor} from './video';
import {AudioProcessor} from './audio';
const MEDIA_TYPE = {
IMG_MEDIA_TYPE: 'img',
VIDEO_MEDIA_TYPE: 'video',
AUDIO_MEDIA_TYPE: 'audio',
} as const;
const MEDIA_TAG = {
IMG: 'IMG',
VIDEO: 'VIDEO',
AUDIO: 'AUDIO',
} as const;
interface MediaLoaderProps {
children: ReactNode;
loadingStrategy: (
mediaHTMLElements: RefObject<MediaHTMLElement>[],
loadMedia: (mediaHTMLElement: MediaHTMLElement) => void
) => void;
}
const imageProcessor = new ImageProcessor();
const videoProcessor = new VideoProcessor();
const audioProcessor = new AudioProcessor();
type MediaTypeKey = (typeof MEDIA_TYPE)[keyof typeof MEDIA_TYPE];
type MediaTagKey = (typeof MEDIA_TAG)[keyof typeof MEDIA_TAG];
const mediaTypeToProcessor: Record<MediaTypeKey, MediaProcessor> = {
[MEDIA_TYPE.IMG_MEDIA_TYPE]: imageProcessor,
[MEDIA_TYPE.VIDEO_MEDIA_TYPE]: videoProcessor,
[MEDIA_TYPE.AUDIO_MEDIA_TYPE]: audioProcessor,
};
const mediaTagToProcessor: Record<MediaTagKey, MediaProcessor> = {
[MEDIA_TAG.IMG]: imageProcessor,
[MEDIA_TAG.VIDEO]: videoProcessor,
[MEDIA_TAG.AUDIO]: audioProcessor,
};
const MediaLoader = ({children, loadingStrategy: triggerFunction}: MediaLoaderProps) => {
const mediaHTMLElements = useRef<RefObject<MediaHTMLElement>[]>([]);
const getChildren = (currentChildren: ReactNode): ReactElement[] => {
const childrenArray = Children.toArray(currentChildren) as ReactElement[];
const mediaChildren = Children.map(childrenArray, (child) => {
const mediaProcessor = mediaTypeToProcessor[child.type as MediaTypeKey];
if (mediaProcessor) {
const [element, ref] = mediaProcessor.getProcessedChildren(child);
mediaHTMLElements.current.push(ref);
return element;
} else {
let result = child;
if (child?.props?.children) {
result = cloneElement(child, {
children: getChildren(child.props.children),
});
}
return result;
}
});
return mediaChildren;
};
useEffect(() => {
if (triggerFunction) {
triggerFunction(mediaHTMLElements.current, loadMedia);
}
}, []);
return <>{getChildren(children)}</>;
};
const loadMedia = (mediaHTMLElement: MediaHTMLElement) => {
const mediaProcessor = mediaTagToProcessor[mediaHTMLElement.tagName as MediaTagKey];
if (mediaProcessor) {
mediaProcessor.loadMedia(mediaHTMLElement);
}
};
export default MediaLoader;
|