UNPKG

audiocard

Version:

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

206 lines (196 loc) 11 kB
'use strict'; var jsxRuntime = require('react/jsx-runtime'); var React = require('react'); 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); const SkipBack = ({ seconds, ...rest }) => (jsxRuntime.jsxs("svg", { viewBox: "0 0 71 71", ...rest, children: [jsxRuntime.jsx("defs", { children: jsxRuntime.jsx("clipPath", { id: "prefix__a", children: jsxRuntime.jsx("path", { d: "M.5 71.5V.5h71v71z" }) }) }), jsxRuntime.jsxs("g", { clipPath: "url(#prefix__a)", transform: "matrix(-1 0 0 1 71 0)", children: [jsxRuntime.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" }), jsxRuntime.jsx("path", { d: "M61.5 35.5h-6M12.5 35.5h-6M34.5 57.5v6", fill: "none", strokeWidth: 3, stroke: "currentColor" }), jsxRuntime.jsx("path", { d: "M34 1l11 7-11 7v-1V0zM45 1l12 7-12 7v-1V0z", fill: "currentColor" })] }), jsxRuntime.jsx("text", { x: 23, y: 44, fontSize: 23, fill: "currentColor", className: "prefix__svgcentertext", children: seconds })] })); const SkipForward = ({ seconds, ...rest }) => (jsxRuntime.jsxs("svg", { viewBox: "0 0 71 71", ...rest, children: [jsxRuntime.jsx("defs", { children: jsxRuntime.jsx("clipPath", { id: "prefix__a", children: jsxRuntime.jsx("path", { d: "M.5 71.5V.5h71v71z" }) }) }), jsxRuntime.jsxs("g", { clipPath: "url(#prefix__a)", children: [jsxRuntime.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" }), jsxRuntime.jsx("path", { d: "M61.5 35.5h-6M12.5 35.5h-6M34.5 57.5v6", fill: "none", strokeWidth: 3, stroke: "currentColor" }), jsxRuntime.jsx("path", { d: "M34 1l11 7-11 7v-1V0zM45 1l12 7-12 7v-1V0z", fill: "currentColor" })] }), jsxRuntime.jsx("text", { x: 22, y: 44, fontSize: 23, fill: "currentColor", className: "prefix__svgcentertext", children: seconds })] })); const Play = (props) => (jsxRuntime.jsx("svg", { viewBox: "0 0 1.25 1.25", display: "block", ...props, children: jsxRuntime.jsx("path", { fill: "currentColor", d: "M.25.125l1 .5-1 .5z" }) })); const Pause = (props) => (jsxRuntime.jsx("svg", { viewBox: "0 0 1 1", display: "block", ...props, children: jsxRuntime.jsx("g", { fill: "currentColor", children: jsxRuntime.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 (jsxRuntime.jsxs("div", { className: className, style: containerStyle, children: [art && jsxRuntime.jsx("img", { src: art, alt: title || 'Audio artwork', style: artStyle }), jsxRuntime.jsxs("div", { style: contentStyle, children: [title && jsxRuntime.jsx("div", { style: titleStyle, children: title }), jsxRuntime.jsx("audio", { ref: audioRef, src: source, preload: String(preload), autoPlay: autoplay, onTimeUpdate: handleAudioTimeUpdate, onPlay: handleAudioPlay, onPause: handleAudioPause, onLoadedMetadata: handleAudioLoadedMetadata, style: { display: 'none' } }), jsxRuntime.jsxs("div", { style: controlsStyle, children: [skipBackSeconds !== undefined ? (jsxRuntime.jsx("div", { style: controlStyle, onClick: () => handleSkip(-skipBackSeconds), title: `Skip Back ${skipBackSeconds}s`, children: jsxRuntime.jsx(SkipBack, { seconds: skipBackSeconds }) })) : (jsxRuntime.jsx("div", { style: controlStyle })), jsxRuntime.jsx("div", { style: controlStyle, onClick: handlePlayPause, title: isPlaying ? 'Pause' : 'Play', children: isPlaying ? jsxRuntime.jsx(Pause, {}) : jsxRuntime.jsx(Play, {}) }), skipForwardSeconds !== undefined ? (jsxRuntime.jsx("div", { style: controlStyle, onClick: () => handleSkip(skipForwardSeconds), title: `Skip Forward ${skipForwardSeconds}s`, children: jsxRuntime.jsx(SkipForward, { seconds: skipForwardSeconds }) })) : (jsxRuntime.jsx("div", { style: controlStyle }))] }), link && linkText && (jsxRuntime.jsx("a", { href: link, target: "_blank", rel: "noopener noreferrer", style: linkStyle, children: linkText })), jsxRuntime.jsxs("div", { style: timesStyle, children: [jsxRuntime.jsx("span", { children: Number.isFinite(currentTime) && currentTime >= 0 && Number.isFinite(internalDuration || duration || 0) && (internalDuration || duration || 0) > 0 ? formatTime(currentTime) : jsxRuntime.jsx("span", { style: { visibility: 'hidden' }, children: "0:00" }) }), jsxRuntime.jsx("span", { children: Number.isFinite(internalDuration || duration || 0) && (internalDuration || duration || 0) > 0 ? formatTime(internalDuration || duration || 0) : jsxRuntime.jsx("span", { style: { visibility: 'hidden' }, children: "0:00" }) })] }), jsxRuntime.jsx("div", { style: progressContainerStyle, onClick: handleProgressBarClick, children: jsxRuntime.jsx("div", { style: progressBarStyle, children: jsxRuntime.jsx("div", { style: progressFillStyle }) }) })] })] })); } module.exports = AudioCard; //# sourceMappingURL=audiocard.cjs.development.js.map