mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
feat(site): API reference pages for media elements (#1342)
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
---
|
||||
import MarkdownCode from '@/components/typography/MarkdownCode.astro';
|
||||
|
||||
interface Props {
|
||||
value: string;
|
||||
}
|
||||
|
||||
const { value } = Astro.props;
|
||||
---
|
||||
|
||||
<span class="code-list__item"><MarkdownCode>{value}</MarkdownCode></span>
|
||||
|
||||
<style>
|
||||
/* The separator is a pseudo-element, so it stays glued to its chip with no
|
||||
text node between them — the formatter can't inject a space before the
|
||||
comma, and there is no break opportunity there. The trailing space inside
|
||||
the separator is the only break opportunity, so a wrap always lands after
|
||||
the comma, never before it.
|
||||
|
||||
Keeping the chip on a single line in this dedicated component is what makes
|
||||
it whitespace-clean: the formatter splits the same markup across lines when
|
||||
it sits inside a `.map()` callback, which is what introduced the stray
|
||||
spaces in the first place. */
|
||||
.code-list__item:not(:last-child)::after {
|
||||
content: ", ";
|
||||
}
|
||||
.code-list__item:last-child::after {
|
||||
content: ".";
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
/**
|
||||
* Renders the host properties table for media element API reference.
|
||||
*
|
||||
* Host properties are getter/setter pairs exposed by the media element's host
|
||||
* class. They differ from standard HTML attributes — they're accessed via JS
|
||||
* properties on the element instance.
|
||||
*/
|
||||
import MarkdownCode from '@/components/typography/MarkdownCode.astro';
|
||||
import Table from '@/components/typography/Table.astro';
|
||||
import Tbody from '@/components/typography/Tbody.astro';
|
||||
import Td from '@/components/typography/Td.astro';
|
||||
import Th from '@/components/typography/Th.astro';
|
||||
import Thead from '@/components/typography/Thead.astro';
|
||||
import Tr from '@/components/typography/Tr.astro';
|
||||
import type { HostPropertyDef } from '@/types/media-reference';
|
||||
import DetailRow from './DetailRow.astro';
|
||||
|
||||
interface Props {
|
||||
hostProperties: Record<string, HostPropertyDef>;
|
||||
componentName: string;
|
||||
}
|
||||
|
||||
const { hostProperties, componentName } = Astro.props;
|
||||
|
||||
const entries = Object.entries(hostProperties).sort(([a], [b]) => a.localeCompare(b));
|
||||
---
|
||||
|
||||
<Table maxWidth={false} outerClass="my-6">
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>Property</Th>
|
||||
<Th>Type</Th>
|
||||
<Th>Default</Th>
|
||||
<Th>Details</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{
|
||||
entries.map(([name, def]) => {
|
||||
const id = `${componentName}-${name}`;
|
||||
const readonlyNote = def.readonly ? "Read-only." : undefined;
|
||||
const description =
|
||||
[readonlyNote, def.description].filter(Boolean).join(" ") ||
|
||||
undefined;
|
||||
|
||||
return (
|
||||
<DetailRow
|
||||
id={id}
|
||||
name={name}
|
||||
type={def.type}
|
||||
description={description}
|
||||
colspan={4}
|
||||
>
|
||||
<Td class="align-top">
|
||||
<MarkdownCode>{name}</MarkdownCode>
|
||||
</Td>
|
||||
<Td class="align-top">
|
||||
<MarkdownCode>{def.type}</MarkdownCode>
|
||||
</Td>
|
||||
<Td class="align-top">
|
||||
<MarkdownCode>{def.default ?? "—"}</MarkdownCode>
|
||||
</Td>
|
||||
</DetailRow>
|
||||
);
|
||||
})
|
||||
}
|
||||
</Tbody>
|
||||
</Table>
|
||||
@@ -0,0 +1,263 @@
|
||||
---
|
||||
import { getEntry } from 'astro:content';
|
||||
import { kebabCase } from 'es-toolkit/string';
|
||||
import ContentWidth from '@/components/frames/ContentWidth.astro';
|
||||
import A from '@/components/typography/A.astro';
|
||||
import H2 from '@/components/typography/H2Markdown.astro';
|
||||
import H3 from '@/components/typography/H3Markdown.astro';
|
||||
import MarkdownCode from '@/components/typography/MarkdownCode.astro';
|
||||
import P from '@/components/typography/P.astro';
|
||||
import Table from '@/components/typography/Table.astro';
|
||||
import Tbody from '@/components/typography/Tbody.astro';
|
||||
import Td from '@/components/typography/Td.astro';
|
||||
import Th from '@/components/typography/Th.astro';
|
||||
import Thead from '@/components/typography/Thead.astro';
|
||||
import Tr from '@/components/typography/Tr.astro';
|
||||
import type { MediaReference } from '@/types/media-reference';
|
||||
import { createMediaReferenceModel } from '@/utils/mediaReferenceModel';
|
||||
import ApiCSSVarsTable from './ApiCSSVarsTable.astro';
|
||||
import CodeChip from './CodeChip.astro';
|
||||
import MediaHostPropsTable from './MediaHostPropsTable.astro';
|
||||
|
||||
interface Props {
|
||||
media: string;
|
||||
}
|
||||
|
||||
const { media } = Astro.props;
|
||||
const framework = Astro.params.framework === 'react' ? 'react' : 'html';
|
||||
|
||||
const entry = await getEntry('mediaReference', kebabCase(media));
|
||||
const ref: MediaReference | null = entry?.data ?? null;
|
||||
if (!ref) return;
|
||||
|
||||
const model = createMediaReferenceModel(media, ref);
|
||||
if (!model) return;
|
||||
|
||||
const hostPropsSection = model.sections.find((s: { key: string }) => s.key === 'hostProperties');
|
||||
const nativeAttrsSection = model.sections.find((s: { key: string }) => s.key === 'nativeAttributes');
|
||||
const eventsSection = model.sections.find((s: { key: string }) => s.key === 'events');
|
||||
const methodsSection = model.sections.find((s: { key: string }) => s.key === 'methods');
|
||||
const cssVarsSection = model.sections.find((s: { key: string }) => s.key === 'cssCustomProperties');
|
||||
const mediaTag = `<${ref.mediaType}>`;
|
||||
|
||||
const mdnElementUrl = `https://developer.mozilla.org/en-US/docs/Web/HTML/Element/${ref.mediaType}`;
|
||||
const mdnEventsUrl = 'https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement#events';
|
||||
const mdnMethodsUrl = 'https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement';
|
||||
|
||||
// React prop spellings for the native attributes that differ from their HTML
|
||||
// attribute names.
|
||||
const REACT_PROP_NAMES: Record<string, string> = {
|
||||
autoplay: 'autoPlay',
|
||||
playsinline: 'playsInline',
|
||||
crossorigin: 'crossOrigin',
|
||||
};
|
||||
|
||||
const sortedNativeAttributes = [...(ref.nativeAttributes ?? [])].sort();
|
||||
const attributeNames =
|
||||
framework === 'react' ? sortedNativeAttributes.map((attr) => REACT_PROP_NAMES[attr] ?? attr) : sortedNativeAttributes;
|
||||
|
||||
const sortedNativeEvents = [...(ref.events.native ?? [])].sort();
|
||||
const sortedMethods = [...(ref.methods ?? [])].sort();
|
||||
|
||||
// React exposes synthetic event props for the standard media events only.
|
||||
// Track-list and Picture-in-Picture events have no prop and must be handled
|
||||
// with a ref + addEventListener. Source: @types/react DOMAttributes.
|
||||
const REACT_MEDIA_EVENTS = new Set([
|
||||
'abort',
|
||||
'canplay',
|
||||
'canplaythrough',
|
||||
'durationchange',
|
||||
'emptied',
|
||||
'ended',
|
||||
'error',
|
||||
'loadeddata',
|
||||
'loadedmetadata',
|
||||
'loadstart',
|
||||
'pause',
|
||||
'play',
|
||||
'playing',
|
||||
'progress',
|
||||
'ratechange',
|
||||
'resize',
|
||||
'seeked',
|
||||
'seeking',
|
||||
'stalled',
|
||||
'suspend',
|
||||
'timeupdate',
|
||||
'volumechange',
|
||||
'waiting',
|
||||
]);
|
||||
const reactMappedEvents = sortedNativeEvents.filter((event) => REACT_MEDIA_EVENTS.has(event));
|
||||
const reactRefOnlyEvents = sortedNativeEvents.filter((event) => !REACT_MEDIA_EVENTS.has(event));
|
||||
|
||||
const elementSpecificEvents = ref.events.elementSpecific;
|
||||
---
|
||||
|
||||
<ContentWidth>
|
||||
<H2 id={model.heading.id}>{model.heading.text}</H2>
|
||||
|
||||
{
|
||||
hostPropsSection && (
|
||||
<>
|
||||
<H3 id={hostPropsSection.id}>{hostPropsSection.title}</H3>
|
||||
<MediaHostPropsTable
|
||||
hostProperties={ref.hostProperties}
|
||||
componentName={media}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
nativeAttrsSection && framework === "html" && (
|
||||
<>
|
||||
<H3 id={nativeAttrsSection.id}>{nativeAttrsSection.title}</H3>
|
||||
<P>
|
||||
Forwards these media attributes to the internal{" "}
|
||||
<MarkdownCode>{mediaTag}</MarkdownCode> element. The standard ones
|
||||
behave as described in the{" "}
|
||||
<A href={mdnElementUrl}>MDN media attributes reference</A>:{" "}
|
||||
<span class="code-list">
|
||||
{attributeNames.map((attr) => (
|
||||
<CodeChip value={attr} />
|
||||
))}
|
||||
</span>
|
||||
</P>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
nativeAttrsSection && framework === "react" && (
|
||||
<>
|
||||
<H3 id={nativeAttrsSection.id}>{nativeAttrsSection.title}</H3>
|
||||
<P>
|
||||
Renders a native <MarkdownCode>{mediaTag}</MarkdownCode> element and
|
||||
accepts these media attributes as React props. The standard ones
|
||||
behave as described in the{" "}
|
||||
<A href={mdnElementUrl}>MDN media attributes reference</A>:{" "}
|
||||
<span class="code-list">
|
||||
{attributeNames.map((attr) => (
|
||||
<CodeChip value={attr} />
|
||||
))}
|
||||
</span>
|
||||
</P>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
eventsSection && (
|
||||
<>
|
||||
<H3 id={eventsSection.id}>{eventsSection.title}</H3>
|
||||
{framework === "html" ? (
|
||||
<P>
|
||||
Re-dispatches these media events from the internal{" "}
|
||||
<MarkdownCode>{mediaTag}</MarkdownCode> element, so you can listen
|
||||
for them directly. See <A href={mdnEventsUrl}>media events</A>:{" "}
|
||||
<span class="code-list">
|
||||
{sortedNativeEvents.map((event) => (
|
||||
<CodeChip value={event} />
|
||||
))}
|
||||
</span>
|
||||
</P>
|
||||
) : (
|
||||
<>
|
||||
<P>
|
||||
Handle these media events with React event props (e.g.{" "}
|
||||
<MarkdownCode>onPlay</MarkdownCode>,{" "}
|
||||
<MarkdownCode>onTimeUpdate</MarkdownCode>). See{" "}
|
||||
<A href={mdnEventsUrl}>media events</A>:{" "}
|
||||
<span class="code-list">
|
||||
{reactMappedEvents.map((event) => (
|
||||
<CodeChip value={event} />
|
||||
))}
|
||||
</span>
|
||||
</P>
|
||||
{reactRefOnlyEvents.length > 0 && (
|
||||
<P>
|
||||
These media events have no React prop — attach a listener
|
||||
through a <MarkdownCode>ref</MarkdownCode> with{" "}
|
||||
<MarkdownCode>addEventListener</MarkdownCode>:{" "}
|
||||
<span class="code-list">
|
||||
{reactRefOnlyEvents.map((event) => (
|
||||
<CodeChip value={event} />
|
||||
))}
|
||||
</span>
|
||||
</P>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{elementSpecificEvents.length === 0 ? (
|
||||
<P>
|
||||
This element dispatches no events beyond the media events above.
|
||||
</P>
|
||||
) : (
|
||||
<>
|
||||
<P>Beyond the media events above, this element emits:</P>
|
||||
<Table maxWidth={false} outerClass="my-6">
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>Event</Th>
|
||||
<Th>Description</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{elementSpecificEvents.map((event) => (
|
||||
<Tr>
|
||||
<Td class="align-top">
|
||||
<MarkdownCode>{event.name}</MarkdownCode>
|
||||
</Td>
|
||||
<Td class="align-top">{event.description ?? ""}</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
{framework === "react" && (
|
||||
<P>
|
||||
These events have no built-in React prop. Attach a listener to
|
||||
the element through a <MarkdownCode>ref</MarkdownCode> with{" "}
|
||||
<MarkdownCode>addEventListener</MarkdownCode>.
|
||||
</P>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
methodsSection && (
|
||||
<>
|
||||
<H3 id={methodsSection.id}>{methodsSection.title}</H3>
|
||||
<P>
|
||||
Supports these methods from the native media API. See{" "}
|
||||
<A href={mdnMethodsUrl}>HTMLMediaElement</A> for the core methods:{" "}
|
||||
<span class="code-list">
|
||||
{sortedMethods.map((method) => (
|
||||
<CodeChip value={method} />
|
||||
))}
|
||||
</span>
|
||||
</P>
|
||||
{framework === "react" && (
|
||||
<P>
|
||||
In React, call these through a <MarkdownCode>ref</MarkdownCode> to
|
||||
the element.
|
||||
</P>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
cssVarsSection && (
|
||||
<>
|
||||
<H3 id={cssVarsSection.id}>{cssVarsSection.title}</H3>
|
||||
<ApiCSSVarsTable
|
||||
cssCustomProperties={ref.cssCustomProperties}
|
||||
componentName={media}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
</ContentWidth>
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
import HtmlDemo from '@/components/docs/demos/HtmlDemo.astro';
|
||||
import html from './BasicUsage.html?raw';
|
||||
---
|
||||
|
||||
<HtmlDemo html={html} />
|
||||
<script>
|
||||
import "./BasicUsage.ts";
|
||||
</script>
|
||||
@@ -0,0 +1,5 @@
|
||||
.container {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<div class="container">
|
||||
<background-video
|
||||
src="https://stream.mux.com/601n4w1fq88NJiVpzvrQQeQfNnnjjfKMIN7dCGAEarTs/highest.mp4"
|
||||
></background-video>
|
||||
</div>
|
||||
@@ -0,0 +1 @@
|
||||
import '@videojs/html/media/background-video';
|
||||
@@ -0,0 +1,5 @@
|
||||
.container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { BackgroundVideo } from '@videojs/react/media/background-video';
|
||||
|
||||
export default function BasicUsage() {
|
||||
return (
|
||||
<div className="container">
|
||||
<BackgroundVideo src="https://stream.mux.com/601n4w1fq88NJiVpzvrQQeQfNnnjjfKMIN7dCGAEarTs/highest.mp4" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
import HtmlDemo from '@/components/docs/demos/HtmlDemo.astro';
|
||||
import html from './BasicUsage.html?raw';
|
||||
---
|
||||
|
||||
<HtmlDemo html={html} />
|
||||
<script>
|
||||
import "./BasicUsage.ts";
|
||||
</script>
|
||||
@@ -0,0 +1,6 @@
|
||||
.media-container {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<media-container class="media-container">
|
||||
<dash-video
|
||||
src="https://dash.akamaized.net/akamai/streamroot/050714/Spring_4Ktest.mpd"
|
||||
autoplay
|
||||
muted
|
||||
playsinline
|
||||
loop
|
||||
></dash-video>
|
||||
</media-container>
|
||||
@@ -0,0 +1,2 @@
|
||||
import '@videojs/html/media/container';
|
||||
import '@videojs/html/media/dash-video';
|
||||
@@ -0,0 +1,4 @@
|
||||
.dash-video {
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { DashVideo } from '@videojs/react/media/dash-video';
|
||||
|
||||
export default function BasicUsage() {
|
||||
return (
|
||||
<DashVideo
|
||||
className="dash-video"
|
||||
src="https://dash.akamaized.net/akamai/streamroot/050714/Spring_4Ktest.mpd"
|
||||
autoPlay
|
||||
muted
|
||||
playsInline
|
||||
loop
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
import HtmlDemo from '@/components/docs/demos/HtmlDemo.astro';
|
||||
import html from './BasicUsage.html?raw';
|
||||
---
|
||||
|
||||
<HtmlDemo html={html} />
|
||||
<script>
|
||||
import "./BasicUsage.ts";
|
||||
</script>
|
||||
@@ -0,0 +1,6 @@
|
||||
.media-container {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<media-container class="media-container">
|
||||
<hls-video
|
||||
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM.m3u8"
|
||||
autoplay
|
||||
muted
|
||||
playsinline
|
||||
loop
|
||||
></hls-video>
|
||||
</media-container>
|
||||
@@ -0,0 +1,2 @@
|
||||
import '@videojs/html/media/container';
|
||||
import '@videojs/html/media/hls-video';
|
||||
@@ -0,0 +1,4 @@
|
||||
.hls-video {
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { HlsVideo } from '@videojs/react/media/hls-video';
|
||||
|
||||
export default function BasicUsage() {
|
||||
return (
|
||||
<HlsVideo
|
||||
className="hls-video"
|
||||
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM.m3u8"
|
||||
autoPlay
|
||||
muted
|
||||
playsInline
|
||||
loop
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
import HtmlDemo from '@/components/docs/demos/HtmlDemo.astro';
|
||||
import html from './BasicUsage.html?raw';
|
||||
---
|
||||
|
||||
<HtmlDemo html={html} />
|
||||
<script>
|
||||
import "./BasicUsage.ts";
|
||||
</script>
|
||||
@@ -0,0 +1,4 @@
|
||||
.mux-audio {
|
||||
width: 100%;
|
||||
height: 54px;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<mux-audio
|
||||
class="mux-audio"
|
||||
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM.m3u8"
|
||||
crossorigin="anonymous"
|
||||
controls
|
||||
></mux-audio>
|
||||
@@ -0,0 +1 @@
|
||||
import '@videojs/html/media/mux-audio';
|
||||
@@ -0,0 +1,4 @@
|
||||
.mux-audio {
|
||||
width: 100%;
|
||||
height: 54px;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { MuxAudio } from '@videojs/react/media/mux-audio';
|
||||
|
||||
export default function BasicUsage() {
|
||||
return (
|
||||
<MuxAudio
|
||||
className="mux-audio"
|
||||
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM.m3u8"
|
||||
crossOrigin="anonymous"
|
||||
controls
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
import HtmlDemo from '@/components/docs/demos/HtmlDemo.astro';
|
||||
import html from './BasicUsage.html?raw';
|
||||
---
|
||||
|
||||
<HtmlDemo html={html} />
|
||||
<script>
|
||||
import "./BasicUsage.ts";
|
||||
</script>
|
||||
@@ -0,0 +1,6 @@
|
||||
.media-container {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<media-container class="media-container">
|
||||
<mux-video
|
||||
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM.m3u8"
|
||||
autoplay
|
||||
muted
|
||||
playsinline
|
||||
loop
|
||||
crossorigin="anonymous"
|
||||
></mux-video>
|
||||
</media-container>
|
||||
@@ -0,0 +1,2 @@
|
||||
import '@videojs/html/media/container';
|
||||
import '@videojs/html/media/mux-video';
|
||||
@@ -0,0 +1,4 @@
|
||||
.mux-video {
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { MuxVideo } from '@videojs/react/media/mux-video';
|
||||
|
||||
export default function BasicUsage() {
|
||||
return (
|
||||
<MuxVideo
|
||||
className="mux-video"
|
||||
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM.m3u8"
|
||||
autoPlay
|
||||
muted
|
||||
playsInline
|
||||
loop
|
||||
crossOrigin="anonymous"
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
import HtmlDemo from '@/components/docs/demos/HtmlDemo.astro';
|
||||
import html from './BasicUsage.html?raw';
|
||||
---
|
||||
|
||||
<HtmlDemo html={html} />
|
||||
<script>
|
||||
import "./BasicUsage.ts";
|
||||
</script>
|
||||
@@ -0,0 +1,6 @@
|
||||
.media-container {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<media-container class="media-container">
|
||||
<native-hls-video
|
||||
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM.m3u8"
|
||||
autoplay
|
||||
muted
|
||||
playsinline
|
||||
loop
|
||||
></native-hls-video>
|
||||
</media-container>
|
||||
@@ -0,0 +1,2 @@
|
||||
import '@videojs/html/media/container';
|
||||
import '@videojs/html/media/native-hls-video';
|
||||
@@ -0,0 +1,4 @@
|
||||
.native-hls-video {
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { NativeHlsVideo } from '@videojs/react/media/native-hls-video';
|
||||
|
||||
export default function BasicUsage() {
|
||||
return (
|
||||
<NativeHlsVideo
|
||||
className="native-hls-video"
|
||||
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM.m3u8"
|
||||
autoPlay
|
||||
muted
|
||||
playsInline
|
||||
loop
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
import HtmlDemo from '@/components/docs/demos/HtmlDemo.astro';
|
||||
import html from './BasicUsage.html?raw';
|
||||
---
|
||||
|
||||
<HtmlDemo html={html} />
|
||||
<script>
|
||||
import "./BasicUsage.ts";
|
||||
</script>
|
||||
@@ -0,0 +1,4 @@
|
||||
.simple-hls-audio-only {
|
||||
width: 100%;
|
||||
height: 54px;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<simple-hls-audio-only
|
||||
class="simple-hls-audio-only"
|
||||
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM.m3u8"
|
||||
controls
|
||||
></simple-hls-audio-only>
|
||||
@@ -0,0 +1 @@
|
||||
import '@videojs/html/media/simple-hls-audio-only';
|
||||
@@ -0,0 +1,4 @@
|
||||
.simple-hls-audio-only {
|
||||
width: 100%;
|
||||
height: 54px;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { SimpleHlsAudioOnly } from '@videojs/react/media/simple-hls-audio-only';
|
||||
|
||||
export default function BasicUsage() {
|
||||
return (
|
||||
<SimpleHlsAudioOnly
|
||||
className="simple-hls-audio-only"
|
||||
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM.m3u8"
|
||||
controls
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
import HtmlDemo from '@/components/docs/demos/HtmlDemo.astro';
|
||||
import html from './BasicUsage.html?raw';
|
||||
---
|
||||
|
||||
<HtmlDemo html={html} />
|
||||
<script>
|
||||
import "./BasicUsage.ts";
|
||||
</script>
|
||||
@@ -0,0 +1,6 @@
|
||||
.media-container {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<media-container class="media-container">
|
||||
<simple-hls-video
|
||||
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM.m3u8"
|
||||
autoplay
|
||||
muted
|
||||
playsinline
|
||||
loop
|
||||
></simple-hls-video>
|
||||
</media-container>
|
||||
@@ -0,0 +1,2 @@
|
||||
import '@videojs/html/media/container';
|
||||
import '@videojs/html/media/simple-hls-video';
|
||||
@@ -0,0 +1,4 @@
|
||||
.simple-hls-video {
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { SimpleHlsVideo } from '@videojs/react/media/simple-hls-video';
|
||||
|
||||
export default function BasicUsage() {
|
||||
return (
|
||||
<SimpleHlsVideo
|
||||
className="simple-hls-video"
|
||||
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM.m3u8"
|
||||
autoPlay
|
||||
muted
|
||||
playsInline
|
||||
loop
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user