UNPKG

@vime/react

Version:

React bindings for the Vime media player.

77 lines (76 loc) 3.45 kB
var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; /* eslint-disable react/prop-types */ import composeRefs from '@seznam/compose-react-refs'; import { createElement, forwardRef, useCallback, useEffect, useMemo, useRef, useState, } from 'react'; export const define = (tagName, clazz) => { const isClient = typeof window !== 'undefined'; if (isClient && !customElements.get(tagName)) customElements.define(tagName, clazz); }; const dashToPascalCase = (str) => str .toLowerCase() .split('-') .map(segment => segment.charAt(0).toUpperCase() + segment.slice(1)) .join(''); const isEvent = (prop) => { var _a; return prop.indexOf('on') === 0 && prop[2] === ((_a = prop[2]) === null || _a === void 0 ? void 0 : _a.toUpperCase()); }; const toDomEventName = (prop) => prop.charAt(2).toLowerCase() + prop.substring(3); export function createComponent(tagName, componentProps) { const Component = forwardRef((_a, forwardedRef) => { var { children } = _a, props = __rest(_a, ["children"]); const [ref, setRef] = useState(null); const setRefCb = useCallback((node) => { setRef(node); }, []); const eventHandlers = useRef(new Map()); const domProps = useMemo(() => Object.keys(props) .filter(prop => !componentProps.has(prop) && !isEvent(prop)) .reduce((p, c) => (Object.assign(Object.assign({}, p), { [c]: props[c] })), {}), [props]); const wcProps = useMemo(() => Object.keys(props) .filter(prop => componentProps.has(prop) || isEvent(prop)) .reduce((p, c) => (Object.assign(Object.assign({}, p), { [c]: props[c] })), {}), [props]); const listen = useCallback((prop, handler) => { var _a; const domEvent = toDomEventName(prop); (_a = eventHandlers.current.get(domEvent)) === null || _a === void 0 ? void 0 : _a(); if (!ref || !handler) return; ref.addEventListener(domEvent, handler); eventHandlers.current.set(domEvent, () => { ref.removeEventListener(domEvent, handler); }); }, [ref]); const cleanup = useCallback(() => { eventHandlers.current.forEach(fn => fn()); eventHandlers.current.clear(); }, []); useEffect(() => () => { cleanup(); }, []); useEffect(() => { if (!ref) return; Object.keys(wcProps).forEach(prop => { if (isEvent(prop)) { listen(prop, wcProps[prop]); return; } if (ref[prop] !== wcProps[prop]) { ref[prop] = wcProps[prop]; } }); }, [ref, wcProps]); return createElement(tagName, Object.assign({ ref: composeRefs(setRefCb, forwardedRef) }, domProps), children); }); Component.displayName = dashToPascalCase(tagName); return Component; }