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