mirror of
https://github.com/zoriya/v10.git
synced 2026-08-10 16:09:18 +00:00
feat(site): feature and preset reference UI + docs integration (#1258)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
5301202a18
commit
d4b805ea69
@@ -0,0 +1,228 @@
|
||||
---
|
||||
import { getCollection } from 'astro:content';
|
||||
import ContentWidth from '@/components/frames/ContentWidth.astro';
|
||||
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 { isValidFramework } from '@/types/docs';
|
||||
import type { PresetReference as PresetReferenceType } from '@/types/preset-reference';
|
||||
import DocsLink from '../DocsLink.astro';
|
||||
import FrameworkCase from '../FrameworkCase.astro';
|
||||
import InlineMarkdown from './InlineMarkdown.astro';
|
||||
|
||||
const entries = await getCollection('presetReference');
|
||||
const PINNED_PRESETS = ['video', 'audio'];
|
||||
const presets: PresetReferenceType[] = entries
|
||||
.map((e) => e.data)
|
||||
.sort((a, b) => {
|
||||
const aPin = PINNED_PRESETS.indexOf(a.name);
|
||||
const bPin = PINNED_PRESETS.indexOf(b.name);
|
||||
if (aPin !== -1 && bPin !== -1) return aPin - bPin;
|
||||
if (aPin !== -1) return -1;
|
||||
if (bPin !== -1) return 1;
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
|
||||
const { framework } = Astro.params;
|
||||
const pkg = framework && isValidFramework(framework) ? `@videojs/${framework}` : '@videojs/html';
|
||||
|
||||
/**
|
||||
* Feature slug overrides for cases where kebabCase(featureName) doesn't
|
||||
* match the docs page slug.
|
||||
*/
|
||||
const FEATURE_SLUG_OVERRIDES: Record<string, string> = {
|
||||
textTrack: 'text-tracks',
|
||||
};
|
||||
|
||||
/**
|
||||
* Native media elements used by presets that don't have a custom element.
|
||||
* The pipeline can't detect these since they're not classes with `static tagName`.
|
||||
*/
|
||||
const NATIVE_MEDIA_ELEMENTS: Record<string, string> = {
|
||||
video: 'video',
|
||||
audio: 'audio',
|
||||
};
|
||||
|
||||
function featureDocsSlug(featureName: string): string {
|
||||
const override = FEATURE_SLUG_OVERRIDES[featureName];
|
||||
if (override) return `reference/feature-${override}`;
|
||||
const kebab = featureName.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
|
||||
return `reference/feature-${kebab}`;
|
||||
}
|
||||
|
||||
function htmlMediaElement(preset: PresetReferenceType): string | undefined {
|
||||
return preset.html.mediaElement ?? NATIVE_MEDIA_ELEMENTS[preset.name];
|
||||
}
|
||||
|
||||
const listFormat = new Intl.ListFormat('en', { style: 'long', type: 'unit' });
|
||||
---
|
||||
|
||||
<ContentWidth>
|
||||
<Table maxWidth={false} outerClass="my-6">
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>Import</Th>
|
||||
<Th>Description</Th>
|
||||
<Th><span class="sr-only">Details</span></Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{
|
||||
presets.map((preset) => {
|
||||
const id = `preset-${preset.name}`;
|
||||
const htmlMedia = htmlMediaElement(preset);
|
||||
return (
|
||||
<>
|
||||
<Tr
|
||||
id={id}
|
||||
data-preset-row
|
||||
class="group cursor-pointer data-expanded:border-transparent dark:data-expanded:border-transparent intent:bg-manila-75 dark:intent:bg-soot"
|
||||
>
|
||||
<Td class="align-top">
|
||||
<MarkdownCode>{`${pkg}/${preset.name}`}</MarkdownCode>
|
||||
</Td>
|
||||
<Td class="align-top">
|
||||
{preset.description && (
|
||||
<InlineMarkdown content={preset.description} />
|
||||
)}
|
||||
</Td>
|
||||
<Td class="align-top">
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-controls={`${id}-detail`}
|
||||
aria-label={`Details for ${preset.name} preset`}
|
||||
data-preset-toggle
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="inline-block group-data-expanded:rotate-90"
|
||||
>
|
||||
▸
|
||||
</span>
|
||||
</button>
|
||||
</Td>
|
||||
</Tr>
|
||||
<Tr id={`${id}-detail`} hidden>
|
||||
<Td colspan="3" class="p-0">
|
||||
<div class="px-6 py-4">
|
||||
<dl
|
||||
class="grid grid-cols-(--grid-cols) gap-x-6 gap-y-3"
|
||||
style="--grid-cols: auto 1fr"
|
||||
>
|
||||
<dt class="font-bold">Feature bundle</dt>
|
||||
<dd>
|
||||
<MarkdownCode>{preset.featureBundle}</MarkdownCode>
|
||||
</dd>
|
||||
|
||||
<dt class="font-bold">Features</dt>
|
||||
<dd>
|
||||
{preset.features.length > 0
|
||||
? listFormat
|
||||
.formatToParts(preset.features)
|
||||
.map((part) =>
|
||||
part.type === "element" ? (
|
||||
<DocsLink
|
||||
class="inline-block whitespace-nowrap"
|
||||
slug={featureDocsSlug(part.value)}
|
||||
>
|
||||
{part.value}
|
||||
</DocsLink>
|
||||
) : (
|
||||
<span>{part.value}</span>
|
||||
),
|
||||
)
|
||||
: "–"}
|
||||
</dd>
|
||||
|
||||
<dt class="font-bold">Skins</dt>
|
||||
<dd>
|
||||
<FrameworkCase frameworks={["react"]}>
|
||||
{preset.react.skins.length > 0
|
||||
? listFormat
|
||||
.formatToParts(
|
||||
preset.react.skins.map((s) => s.name),
|
||||
)
|
||||
.map((part) =>
|
||||
part.type === "element" ? (
|
||||
<MarkdownCode class="inline-block whitespace-nowrap">{`<${part.value}>`}</MarkdownCode>
|
||||
) : (
|
||||
<span>{part.value}</span>
|
||||
),
|
||||
)
|
||||
: "–"}
|
||||
</FrameworkCase>
|
||||
<FrameworkCase frameworks={["html"]}>
|
||||
{preset.html.skins.length > 0
|
||||
? listFormat
|
||||
.formatToParts(
|
||||
preset.html.skins.map(
|
||||
(s) => s.tagName ?? s.name,
|
||||
),
|
||||
)
|
||||
.map((part) =>
|
||||
part.type === "element" ? (
|
||||
<MarkdownCode class="inline-block whitespace-nowrap">{`<${part.value}>`}</MarkdownCode>
|
||||
) : (
|
||||
<span>{part.value}</span>
|
||||
),
|
||||
)
|
||||
: "–"}
|
||||
</FrameworkCase>
|
||||
</dd>
|
||||
|
||||
<dt class="font-bold">Default media element</dt>
|
||||
<dd>
|
||||
<FrameworkCase frameworks={["react"]}>
|
||||
<MarkdownCode>{`<${preset.react.mediaElement}>`}</MarkdownCode>
|
||||
</FrameworkCase>
|
||||
<FrameworkCase frameworks={["html"]}>
|
||||
{htmlMedia ? (
|
||||
<MarkdownCode>{`<${htmlMedia}>`}</MarkdownCode>
|
||||
) : (
|
||||
"–"
|
||||
)}
|
||||
</FrameworkCase>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</Td>
|
||||
</Tr>
|
||||
</>
|
||||
);
|
||||
})
|
||||
}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</ContentWidth>
|
||||
|
||||
<script>
|
||||
document
|
||||
.querySelectorAll<HTMLButtonElement>("[data-preset-toggle]")
|
||||
.forEach((button) => {
|
||||
button.addEventListener("click", () => {
|
||||
const expanded = button.getAttribute("aria-expanded") === "true";
|
||||
button.setAttribute("aria-expanded", String(!expanded));
|
||||
|
||||
const row = button.closest<HTMLTableRowElement>("[data-preset-row]");
|
||||
if (row) row.toggleAttribute("data-expanded", !expanded);
|
||||
|
||||
const detail = document.getElementById(
|
||||
button.getAttribute("aria-controls")!,
|
||||
);
|
||||
if (detail) detail.hidden = expanded;
|
||||
});
|
||||
});
|
||||
|
||||
document
|
||||
.querySelectorAll<HTMLTableRowElement>("[data-preset-row]")
|
||||
.forEach((row) => {
|
||||
row.addEventListener("click", (e) => {
|
||||
if ((e.target as Element).closest("a, button")) return;
|
||||
row.querySelector<HTMLButtonElement>("[data-preset-toggle]")?.click();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user