audiocard
Version:
Opinionated, responsive, audio player compatible with Twitter Cards - Fully RSC compliant
206 lines (196 loc) • 11 kB
JavaScript
'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