UNPKG

audiocard

Version:

Opinionated, responsive, audio player compatible with Twitter Cards - Fully RSC compliant

778 lines (758 loc) 26.7 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react')) : typeof define === 'function' && define.amd ? define(['exports', 'react'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.AudioCard = {}, global.React)); })(this, (function (exports, React) { 'use strict'; function _interopNamespaceDefault(e) { var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React); var jsxRuntime = {exports: {}}; var reactJsxRuntime_development = {}; /** * @license React * react-jsx-runtime.development.js * * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ var hasRequiredReactJsxRuntime_development; function requireReactJsxRuntime_development () { if (hasRequiredReactJsxRuntime_development) return reactJsxRuntime_development; hasRequiredReactJsxRuntime_development = 1; ((function () { function getComponentNameFromType(type) { if (null == type) return null; if ("function" === typeof type) return type.$$typeof === REACT_CLIENT_REFERENCE ? null : type.displayName || type.name || null; if ("string" === typeof type) return type; switch (type) { case REACT_FRAGMENT_TYPE: return "Fragment"; case REACT_PROFILER_TYPE: return "Profiler"; case REACT_STRICT_MODE_TYPE: return "StrictMode"; case REACT_SUSPENSE_TYPE: return "Suspense"; case REACT_SUSPENSE_LIST_TYPE: return "SuspenseList"; case REACT_ACTIVITY_TYPE: return "Activity"; } if ("object" === typeof type) switch ( ("number" === typeof type.tag && console.error( "Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue." ), type.$$typeof) ) { case REACT_PORTAL_TYPE: return "Portal"; case REACT_CONTEXT_TYPE: return (type.displayName || "Context") + ".Provider"; case REACT_CONSUMER_TYPE: return (type._context.displayName || "Context") + ".Consumer"; case REACT_FORWARD_REF_TYPE: var innerType = type.render; type = type.displayName; type || ((type = innerType.displayName || innerType.name || ""), (type = "" !== type ? "ForwardRef(" + type + ")" : "ForwardRef")); return type; case REACT_MEMO_TYPE: return ( (innerType = type.displayName || null), null !== innerType ? innerType : getComponentNameFromType(type.type) || "Memo" ); case REACT_LAZY_TYPE: innerType = type._payload; type = type._init; try { return getComponentNameFromType(type(innerType)); } catch (x) {} } return null; } function testStringCoercion(value) { return "" + value; } function checkKeyStringCoercion(value) { try { testStringCoercion(value); var JSCompiler_inline_result = !1; } catch (e) { JSCompiler_inline_result = true; } if (JSCompiler_inline_result) { JSCompiler_inline_result = console; var JSCompiler_temp_const = JSCompiler_inline_result.error; var JSCompiler_inline_result$jscomp$0 = ("function" === typeof Symbol && Symbol.toStringTag && value[Symbol.toStringTag]) || value.constructor.name || "Object"; JSCompiler_temp_const.call( JSCompiler_inline_result, "The provided key is an unsupported type %s. This value must be coerced to a string before using it here.", JSCompiler_inline_result$jscomp$0 ); return testStringCoercion(value); } } function getTaskName(type) { if (type === REACT_FRAGMENT_TYPE) return "<>"; if ( "object" === typeof type && null !== type && type.$$typeof === REACT_LAZY_TYPE ) return "<...>"; try { var name = getComponentNameFromType(type); return name ? "<" + name + ">" : "<...>"; } catch (x) { return "<...>"; } } function getOwner() { var dispatcher = ReactSharedInternals.A; return null === dispatcher ? null : dispatcher.getOwner(); } function UnknownOwner() { return Error("react-stack-top-frame"); } function hasValidKey(config) { if (hasOwnProperty.call(config, "key")) { var getter = Object.getOwnPropertyDescriptor(config, "key").get; if (getter && getter.isReactWarning) return false; } return void 0 !== config.key; } function defineKeyPropWarningGetter(props, displayName) { function warnAboutAccessingKey() { specialPropKeyWarningShown || ((specialPropKeyWarningShown = true), console.error( "%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)", displayName )); } warnAboutAccessingKey.isReactWarning = true; Object.defineProperty(props, "key", { get: warnAboutAccessingKey, configurable: true }); } function elementRefGetterWithDeprecationWarning() { var componentName = getComponentNameFromType(this.type); didWarnAboutElementRef[componentName] || ((didWarnAboutElementRef[componentName] = true), console.error( "Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release." )); componentName = this.props.ref; return void 0 !== componentName ? componentName : null; } function ReactElement( type, key, self, source, owner, props, debugStack, debugTask ) { self = props.ref; type = { $$typeof: REACT_ELEMENT_TYPE, type: type, key: key, props: props, _owner: owner }; null !== (void 0 !== self ? self : null) ? Object.defineProperty(type, "ref", { enumerable: false, get: elementRefGetterWithDeprecationWarning }) : Object.defineProperty(type, "ref", { enumerable: false, value: null }); type._store = {}; Object.defineProperty(type._store, "validated", { configurable: false, enumerable: false, writable: true, value: 0 }); Object.defineProperty(type, "_debugInfo", { configurable: false, enumerable: false, writable: true, value: null }); Object.defineProperty(type, "_debugStack", { configurable: false, enumerable: false, writable: true, value: debugStack }); Object.defineProperty(type, "_debugTask", { configurable: false, enumerable: false, writable: true, value: debugTask }); Object.freeze && (Object.freeze(type.props), Object.freeze(type)); return type; } function jsxDEVImpl( type, config, maybeKey, isStaticChildren, source, self, debugStack, debugTask ) { var children = config.children; if (void 0 !== children) if (isStaticChildren) if (isArrayImpl(children)) { for ( isStaticChildren = 0; isStaticChildren < children.length; isStaticChildren++ ) validateChildKeys(children[isStaticChildren]); Object.freeze && Object.freeze(children); } else console.error( "React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead." ); else validateChildKeys(children); if (hasOwnProperty.call(config, "key")) { children = getComponentNameFromType(type); var keys = Object.keys(config).filter(function (k) { return "key" !== k; }); isStaticChildren = 0 < keys.length ? "{key: someKey, " + keys.join(": ..., ") + ": ...}" : "{key: someKey}"; didWarnAboutKeySpread[children + isStaticChildren] || ((keys = 0 < keys.length ? "{" + keys.join(": ..., ") + ": ...}" : "{}"), console.error( 'A props object containing a "key" prop is being spread into JSX:\n let props = %s;\n <%s {...props} />\nReact keys must be passed directly to JSX without using spread:\n let props = %s;\n <%s key={someKey} {...props} />', isStaticChildren, children, keys, children ), (didWarnAboutKeySpread[children + isStaticChildren] = true)); } children = null; void 0 !== maybeKey && (checkKeyStringCoercion(maybeKey), (children = "" + maybeKey)); hasValidKey(config) && (checkKeyStringCoercion(config.key), (children = "" + config.key)); if ("key" in config) { maybeKey = {}; for (var propName in config) "key" !== propName && (maybeKey[propName] = config[propName]); } else maybeKey = config; children && defineKeyPropWarningGetter( maybeKey, "function" === typeof type ? type.displayName || type.name || "Unknown" : type ); return ReactElement( type, children, self, source, getOwner(), maybeKey, debugStack, debugTask ); } function validateChildKeys(node) { "object" === typeof node && null !== node && node.$$typeof === REACT_ELEMENT_TYPE && node._store && (node._store.validated = 1); } var React$1 = React, REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"), REACT_PORTAL_TYPE = Symbol.for("react.portal"), REACT_FRAGMENT_TYPE = Symbol.for("react.fragment"), REACT_STRICT_MODE_TYPE = Symbol.for("react.strict_mode"), REACT_PROFILER_TYPE = Symbol.for("react.profiler"); var REACT_CONSUMER_TYPE = Symbol.for("react.consumer"), REACT_CONTEXT_TYPE = Symbol.for("react.context"), REACT_FORWARD_REF_TYPE = Symbol.for("react.forward_ref"), REACT_SUSPENSE_TYPE = Symbol.for("react.suspense"), REACT_SUSPENSE_LIST_TYPE = Symbol.for("react.suspense_list"), REACT_MEMO_TYPE = Symbol.for("react.memo"), REACT_LAZY_TYPE = Symbol.for("react.lazy"), REACT_ACTIVITY_TYPE = Symbol.for("react.activity"), REACT_CLIENT_REFERENCE = Symbol.for("react.client.reference"), ReactSharedInternals = React$1.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, hasOwnProperty = Object.prototype.hasOwnProperty, isArrayImpl = Array.isArray, createTask = console.createTask ? console.createTask : function () { return null; }; React$1 = { "react-stack-bottom-frame": function (callStackForError) { return callStackForError(); } }; var specialPropKeyWarningShown; var didWarnAboutElementRef = {}; var unknownOwnerDebugStack = React$1["react-stack-bottom-frame"].bind( React$1, UnknownOwner )(); var unknownOwnerDebugTask = createTask(getTaskName(UnknownOwner)); var didWarnAboutKeySpread = {}; reactJsxRuntime_development.Fragment = REACT_FRAGMENT_TYPE; reactJsxRuntime_development.jsx = function (type, config, maybeKey, source, self) { var trackActualOwner = 1e4 > ReactSharedInternals.recentlyCreatedOwnerStacks++; return jsxDEVImpl( type, config, maybeKey, false, source, self, trackActualOwner ? Error("react-stack-top-frame") : unknownOwnerDebugStack, trackActualOwner ? createTask(getTaskName(type)) : unknownOwnerDebugTask ); }; reactJsxRuntime_development.jsxs = function (type, config, maybeKey, source, self) { var trackActualOwner = 1e4 > ReactSharedInternals.recentlyCreatedOwnerStacks++; return jsxDEVImpl( type, config, maybeKey, true, source, self, trackActualOwner ? Error("react-stack-top-frame") : unknownOwnerDebugStack, trackActualOwner ? createTask(getTaskName(type)) : unknownOwnerDebugTask ); }; }))(); return reactJsxRuntime_development; } var hasRequiredJsxRuntime; function requireJsxRuntime () { if (hasRequiredJsxRuntime) return jsxRuntime.exports; hasRequiredJsxRuntime = 1; { jsxRuntime.exports = requireReactJsxRuntime_development(); } return jsxRuntime.exports; } var jsxRuntimeExports = requireJsxRuntime(); const SkipBack = ({ seconds, ...rest }) => jsxRuntimeExports.jsxs("svg", { viewBox: "0 0 71 71", ...rest, children: [jsxRuntimeExports.jsx("defs", { children: jsxRuntimeExports.jsx("clipPath", { id: "prefix__a", children: jsxRuntimeExports.jsx("path", { d: "M.5 71.5V.5h71v71z" }) }) }), jsxRuntimeExports.jsxs("g", { clipPath: "url(#prefix__a)", transform: "matrix(-1 0 0 1 71 0)", children: [jsxRuntimeExports.jsx("path", { d: "M61.5 35.5c0 7-2 14-8 20-10 11-28 11-39 0-10-11-10-28 0-39 6-6 14-8 21-8h1", fill: "none", strokeWidth: 3, stroke: "currentColor", strokeLinecap: "square" }), jsxRuntimeExports.jsx("path", { d: "M61.5 35.5h-6M12.5 35.5h-6M34.5 57.5v6", fill: "none", strokeWidth: 3, stroke: "currentColor" }), jsxRuntimeExports.jsx("path", { d: "M34 1l11 7-11 7v-1V0zM45 1l12 7-12 7v-1V0z", fill: "currentColor" })] }), jsxRuntimeExports.jsx("text", { x: 23, y: 44, fontSize: 23, fill: "currentColor", className: "prefix__svgcentertext", children: seconds })] }); const SkipForward = ({ seconds, ...rest }) => jsxRuntimeExports.jsxs("svg", { viewBox: "0 0 71 71", ...rest, children: [jsxRuntimeExports.jsx("defs", { children: jsxRuntimeExports.jsx("clipPath", { id: "prefix__a", children: jsxRuntimeExports.jsx("path", { d: "M.5 71.5V.5h71v71z" }) }) }), jsxRuntimeExports.jsxs("g", { clipPath: "url(#prefix__a)", children: [jsxRuntimeExports.jsx("path", { d: "M61.5 35.5c0 7-2 14-8 20-10 11-28 11-39 0-10-11-10-28 0-39 6-6 14-8 21-8h1", fill: "none", strokeWidth: 3, stroke: "currentColor", strokeLinecap: "square" }), jsxRuntimeExports.jsx("path", { d: "M61.5 35.5h-6M12.5 35.5h-6M34.5 57.5v6", fill: "none", strokeWidth: 3, stroke: "currentColor" }), jsxRuntimeExports.jsx("path", { d: "M34 1l11 7-11 7v-1V0zM45 1l12 7-12 7v-1V0z", fill: "currentColor" })] }), jsxRuntimeExports.jsx("text", { x: 22, y: 44, fontSize: 23, fill: "currentColor", className: "prefix__svgcentertext", children: seconds })] }); const Play = props => jsxRuntimeExports.jsx("svg", { viewBox: "0 0 1.25 1.25", display: "block", ...props, children: jsxRuntimeExports.jsx("path", { fill: "currentColor", d: "M.25.125l1 .5-1 .5z" }) }); const Pause = props => jsxRuntimeExports.jsx("svg", { viewBox: "0 0 1 1", display: "block", ...props, children: jsxRuntimeExports.jsx("g", { fill: "currentColor", children: jsxRuntimeExports.jsx("path", { d: "M.15.15h.262v.7H.15zM.588.15H.85v.7H.588z" }) }) }); const canonicalWidth = 750; const canonicalHeight = 225; const aspectRatio = canonicalWidth / canonicalHeight; function AudioCard({ art, background, className, color = '#666', link, linkText, progressBarBackground = '#ddd', progressBarCompleteBackground = '#aaa', skipBackSeconds, skipForwardSeconds, source, title, duration = 0, currentTime: initialCurrentTime = 0, width = canonicalWidth, autoplay = false, preload = 'auto' }) { const height = width / aspectRatio; const h = value => value * height / canonicalHeight; const w = value => value * width / canonicalWidth; const audioRef = React__namespace.useRef(null); const [isPlaying, setIsPlaying] = React__namespace.useState(false); const [currentTime, setCurrentTime] = React__namespace.useState(initialCurrentTime); const [internalDuration, setInternalDuration] = React__namespace.useState(duration || 0); React__namespace.useEffect(() => { setCurrentTime(initialCurrentTime); }, [initialCurrentTime, setCurrentTime]); React__namespace.useEffect(() => { if (autoplay && audioRef.current) { audioRef.current.play(); } }, [autoplay]); const handlePlayPause = () => { const audio = audioRef.current; if (!audio) return; if (isPlaying) { audio.pause(); } else { audio.play(); } }; const handleSkip = seconds => { const audio = audioRef.current; if (!audio) return; audio.currentTime = Math.max(0, Math.min(internalDuration || duration || 0, audio.currentTime + seconds)); }; const handleAudioTimeUpdate = () => { if (audioRef.current) { setCurrentTime(audioRef.current.currentTime); } }; const handleAudioPlay = () => setIsPlaying(true); const handleAudioPause = () => setIsPlaying(false); const handleAudioLoadedMetadata = () => { if (audioRef.current) { setInternalDuration(audioRef.current.duration); } }; const handleProgressBarClick = e => { const audio = audioRef.current; if (!audio) return; const rect = e.currentTarget.getBoundingClientRect(); const x = e.clientX - rect.left; const percent = x / rect.width; const seekTime = percent * (internalDuration || duration || 0); audio.currentTime = seekTime; }; const progressPercentage = (internalDuration || duration || 0) > 0 ? Math.min(100, Math.max(0, currentTime / (internalDuration || duration || 0) * 100)) : 0; const containerStyle = { width: '100%', maxWidth: '100%', display: 'flex', flexFlow: 'row nowrap', fontFamily: "'San Francisco', 'Helvetica Neue', Helvetica, sans-serif", lineHeight: '1em', height: height, color: color, userSelect: 'none' }; if (background) { containerStyle.backgroundColor = background; } const contentStyle = { flex: 1, display: 'flex', flexFlow: 'column nowrap', padding: 0 }; const controlsStyle = { flex: 1, display: 'flex', flexFlow: 'row nowrap', justifyContent: 'space-between', fontSize: h(16) }; const timesStyle = { display: 'flex', flexFlow: 'row nowrap', justifyContent: 'space-between', margin: '0 5px 5px 5px', fontSize: h(16) }; const controlStyle = { textDecoration: 'none', display: 'flex', flexFlow: 'column nowrap', justifyContent: 'center', margin: '5px', width: '15%', color: color, cursor: 'pointer' }; const artStyle = { height: height, width: height, minHeight: height, minWidth: height }; const titleStyle = { textAlign: 'center', overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis', fontSize: h(24), width: w(canonicalWidth - (art ? canonicalHeight : 0) - 20), margin: h(12) + 'px ' + w(10) + 'px ' + h(12) + 'px ' + w(10) + 'px', minHeight: h(30) }; const linkStyle = { display: 'block', textAlign: 'center', fontSize: h(20), color: color, textDecoration: 'none' }; const progressContainerStyle = { position: 'relative', height: h(20), minHeight: h(20), cursor: 'pointer' }; const progressBarStyle = { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, width: '100%', height: '100%', backgroundColor: progressBarBackground, boxShadow: 'inset 1px 1px 2px rgba(0, 0, 0, 0.3)', WebkitAppearance: 'none', MozAppearance: 'none' }; const progressFillStyle = { position: 'absolute', top: 0, left: 0, height: '100%', width: progressPercentage + '%', backgroundColor: progressBarCompleteBackground, transition: 'width 0.1s ease' }; const formatTime = seconds => { if (!Number.isFinite(seconds) || seconds < 0) return '0:00'; const mins = Math.floor(seconds / 60); const secs = Math.floor(seconds % 60); const secsStr = secs < 10 ? '0' + secs : String(secs); return mins + ':' + secsStr; }; return jsxRuntimeExports.jsxs("div", { className: className, style: containerStyle, children: [art && jsxRuntimeExports.jsx("img", { src: art, alt: title || 'Audio artwork', style: artStyle }), jsxRuntimeExports.jsxs("div", { style: contentStyle, children: [title && jsxRuntimeExports.jsx("div", { style: titleStyle, children: title }), jsxRuntimeExports.jsx("audio", { ref: audioRef, src: source, preload: String(preload), autoPlay: autoplay, onTimeUpdate: handleAudioTimeUpdate, onPlay: handleAudioPlay, onPause: handleAudioPause, onLoadedMetadata: handleAudioLoadedMetadata, style: { display: 'none' } }), jsxRuntimeExports.jsxs("div", { style: controlsStyle, children: [skipBackSeconds !== undefined ? jsxRuntimeExports.jsx("div", { style: controlStyle, onClick: () => handleSkip(-skipBackSeconds), title: `Skip Back ${skipBackSeconds}s`, children: jsxRuntimeExports.jsx(SkipBack, { seconds: skipBackSeconds }) }) : jsxRuntimeExports.jsx("div", { style: controlStyle }), jsxRuntimeExports.jsx("div", { style: controlStyle, onClick: handlePlayPause, title: isPlaying ? 'Pause' : 'Play', children: isPlaying ? jsxRuntimeExports.jsx(Pause, {}) : jsxRuntimeExports.jsx(Play, {}) }), skipForwardSeconds !== undefined ? jsxRuntimeExports.jsx("div", { style: controlStyle, onClick: () => handleSkip(skipForwardSeconds), title: `Skip Forward ${skipForwardSeconds}s`, children: jsxRuntimeExports.jsx(SkipForward, { seconds: skipForwardSeconds }) }) : jsxRuntimeExports.jsx("div", { style: controlStyle })] }), link && linkText && jsxRuntimeExports.jsx("a", { href: link, target: "_blank", rel: "noopener noreferrer", style: linkStyle, children: linkText }), jsxRuntimeExports.jsxs("div", { style: timesStyle, children: [jsxRuntimeExports.jsx("span", { children: Number.isFinite(currentTime) && currentTime >= 0 && Number.isFinite(internalDuration || duration || 0) && (internalDuration || duration || 0) > 0 ? formatTime(currentTime) : jsxRuntimeExports.jsx("span", { style: { visibility: 'hidden' }, children: "0:00" }) }), jsxRuntimeExports.jsx("span", { children: Number.isFinite(internalDuration || duration || 0) && (internalDuration || duration || 0) > 0 ? formatTime(internalDuration || duration || 0) : jsxRuntimeExports.jsx("span", { style: { visibility: 'hidden' }, children: "0:00" }) })] }), jsxRuntimeExports.jsx("div", { style: progressContainerStyle, onClick: handleProgressBarClick, children: jsxRuntimeExports.jsx("div", { style: progressBarStyle, children: jsxRuntimeExports.jsx("div", { style: progressFillStyle }) }) })] })] }); } exports.default = AudioCard; Object.defineProperty(exports, '__esModule', { value: true }); })); //# sourceMappingURL=audiocard.umd.development.js.map