feat(site): add TimeSlider, VolumeSlider, Popover API references (#685)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Darius Cepulis
2026-03-03 17:41:35 -06:00
committed by GitHub
co-authored by Claude Opus 4.6
parent 916f4700ae
commit 8ab596ea30
71 changed files with 2093 additions and 98 deletions
@@ -0,0 +1,41 @@
---
/**
* Renders the CSS custom properties table for API reference.
*/
import Table from '@/components/typography/Table.astro';
import Tbody from '@/components/typography/Tbody.astro';
import Th from '@/components/typography/Th.astro';
import Thead from '@/components/typography/Thead.astro';
import Tr from '@/components/typography/Tr.astro';
import type { CSSVarDef } from '@/types/component-reference';
import CSSVarRow from './CSSVarRow.astro';
interface Props {
cssCustomProperties: Record<string, CSSVarDef>;
componentName: string;
}
const { cssCustomProperties, componentName } = Astro.props;
const vars = Object.entries(cssCustomProperties);
---
<Table maxWidth={false} outerClass="my-6">
<Thead>
<Tr>
<Th>Variable</Th>
<Th />
</Tr>
</Thead>
<Tbody>
{
vars.map(([name, def]) => (
<CSSVarRow
name={name}
description={def.description}
componentName={componentName}
/>
))
}
</Tbody>
</Table>
@@ -0,0 +1,21 @@
---
import MarkdownCode from '@/components/typography/MarkdownCode.astro';
import Td from '@/components/typography/Td.astro';
import DetailRow from './DetailRow.astro';
interface Props {
name: string;
description?: string;
componentName: string;
}
const { name, description, componentName } = Astro.props;
const id = `${componentName}-css-var-${name}`;
---
<DetailRow id={id} name={name} type="" description={description} colspan={2}>
<Td class="align-top">
<MarkdownCode>{name}</MarkdownCode>
</Td>
</DetailRow>
@@ -11,6 +11,7 @@ import type { ComponentReference } from '@/types/component-reference';
import { isValidFramework } from '@/types/docs';
import { createComponentReferenceModel } from '@/utils/componentReferenceModel';
import FrameworkCase from '../FrameworkCase.astro';
import ApiCSSVarsTable from './ApiCSSVarsTable.astro';
import ApiDataAttrsTable from './ApiDataAttrsTable.astro';
import ApiPropsTable from './ApiPropsTable.astro';
import ApiStateTable from './ApiStateTable.astro';
@@ -42,6 +43,9 @@ const singleStateSection = !apiReferenceModel.hasParts
const singleDataAttributesSection = !apiReferenceModel.hasParts
? apiReferenceModel.sections.find((section) => section.key === 'dataAttributes')
: null;
const singleCssCustomPropertiesSection = !apiReferenceModel.hasParts
? apiReferenceModel.sections.find((section) => section.key === 'cssCustomProperties')
: null;
---
<ContentWidth>
@@ -52,9 +56,10 @@ const singleDataAttributesSection = !apiReferenceModel.hasParts
const partPropsSection = part.sections.find((section) => section.key === 'props');
const partStateSection = part.sections.find((section) => section.key === 'state');
const partDataAttributesSection = part.sections.find((section) => section.key === 'dataAttributes');
const partCssCustomPropertiesSection = part.sections.find((section) => section.key === 'cssCustomProperties');
return (
<>
<FrameworkCase frameworks={part.frameworks}>
<H3 id={part.id}>
<FrameworkCase frameworks={["react"]}>{part.labelByFramework.react}</FrameworkCase>
<FrameworkCase frameworks={["html"]}>{part.labelByFramework.html}</FrameworkCase>
@@ -95,7 +100,14 @@ const singleDataAttributesSection = !apiReferenceModel.hasParts
<ApiDataAttrsTable dataAttributes={part.data.dataAttributes} componentName={part.componentName} />
</>
)}
</>
{partCssCustomPropertiesSection && (
<>
<H4 id={partCssCustomPropertiesSection.id}>{partCssCustomPropertiesSection.title}</H4>
<ApiCSSVarsTable cssCustomProperties={part.data.cssCustomProperties} componentName={part.componentName} />
</>
)}
</FrameworkCase>
);
})
) : (
@@ -131,6 +143,13 @@ const singleDataAttributesSection = !apiReferenceModel.hasParts
<ApiDataAttrsTable dataAttributes={apiReferenceModel.data.dataAttributes} componentName={component} />
</>
)}
{singleCssCustomPropertiesSection && (
<>
<H3 id={singleCssCustomPropertiesSection.id}>{singleCssCustomPropertiesSection.title}</H3>
<ApiCSSVarsTable cssCustomProperties={apiReferenceModel.data.cssCustomProperties} componentName={component} />
</>
)}
</>
)}
</ContentWidth>
@@ -0,0 +1,10 @@
---
import HtmlDemo from '@/components/docs/demos/HtmlDemo.astro';
import html from './BasicUsage.html?raw';
import './BasicUsage.css';
---
<HtmlDemo html={html} />
<script>
import './BasicUsage.ts';
</script>
@@ -0,0 +1,35 @@
.html-popover-basic,
.html-popover-basic media-container {
display: block;
position: relative;
}
.html-popover-basic video {
width: 100%;
}
.html-popover-basic__bar {
position: absolute;
bottom: 10px;
left: 10px;
}
.html-popover-basic__trigger {
padding: 6px 16px;
background: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(10px);
color: black;
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 9999px;
cursor: pointer;
}
.html-popover-basic__popover {
background: rgba(0, 0, 0, 0.85);
backdrop-filter: blur(10px);
color: white;
border-radius: 8px;
padding: 12px 16px;
font-size: 14px;
white-space: nowrap;
}
@@ -0,0 +1,19 @@
<video-player class="html-popover-basic">
<media-container>
<video
src="https://stream.mux.com/lhnU49l1VGi3zrTAZhDm9LUUxSjpaPW9BL4jY25Kwo4/highest.mp4"
autoplay
muted
playsinline
loop
></video>
<div class="html-popover-basic__bar">
<button type="button" commandfor="popover-demo" class="html-popover-basic__trigger">Settings</button>
<media-popover id="popover-demo" class="html-popover-basic__popover">
<div class="html-popover-basic__popup">
Popover content
</div>
</media-popover>
</div>
</media-container>
</video-player>
@@ -0,0 +1,2 @@
import '@videojs/html/video/player';
import '@videojs/html/ui/popover';
@@ -0,0 +1,45 @@
.react-popover-basic {
position: relative;
}
.react-popover-basic video {
width: 100%;
}
.react-popover-basic__bar {
position: absolute;
bottom: 10px;
left: 10px;
}
.react-popover-basic__trigger {
padding: 6px 16px;
background: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(10px);
color: black;
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 9999px;
cursor: pointer;
}
.react-popover-basic__popup {
/* Reset UA [popover] defaults */
margin: 0;
border: 0;
--media-popover-side-offset: 8px;
background: rgba(0, 0, 0, 0.85);
backdrop-filter: blur(10px);
color: white;
border-radius: 8px;
padding: 12px 16px;
font-size: 14px;
}
.react-popover-basic__arrow {
fill: rgba(0, 0, 0, 0.85);
}
.react-popover-basic__content {
white-space: nowrap;
}
@@ -0,0 +1,31 @@
import { createPlayer, Popover } from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';
import './BasicUsage.css';
const Player = createPlayer({ features: videoFeatures });
export default function BasicUsage() {
return (
<Player.Provider>
<Player.Container className="react-popover-basic">
<Video
src="https://stream.mux.com/lhnU49l1VGi3zrTAZhDm9LUUxSjpaPW9BL4jY25Kwo4/highest.mp4"
autoPlay
muted
playsInline
loop
/>
<div className="react-popover-basic__bar">
<Popover.Root>
<Popover.Trigger className="react-popover-basic__trigger">Settings</Popover.Trigger>
<Popover.Popup className="react-popover-basic__popup">
<Popover.Arrow className="react-popover-basic__arrow" />
<div className="react-popover-basic__content">Popover content</div>
</Popover.Popup>
</Popover.Root>
</div>
</Player.Container>
</Player.Provider>
);
}
@@ -0,0 +1,10 @@
---
import HtmlDemo from '@/components/docs/demos/HtmlDemo.astro';
import html from './BasicUsage.html?raw';
import './BasicUsage.css';
---
<HtmlDemo html={html} />
<script>
import './BasicUsage.ts';
</script>
@@ -0,0 +1,30 @@
.html-time-slider-basic,
.html-time-slider-basic media-container {
display: block;
position: relative;
}
.html-time-slider-basic video {
width: 100%;
}
.html-time-slider-basic__slider {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 20px;
cursor: pointer;
background: rgba(255, 255, 255, 0.3);
}
.html-time-slider-basic__slider::before {
content: "";
position: absolute;
top: 0;
left: 0;
height: 100%;
width: var(--media-slider-fill);
background: white;
pointer-events: none;
}
@@ -0,0 +1,12 @@
<video-player class="html-time-slider-basic">
<media-container>
<video
src="https://stream.mux.com/lhnU49l1VGi3zrTAZhDm9LUUxSjpaPW9BL4jY25Kwo4/highest.mp4"
autoplay
muted
playsinline
loop
></video>
<media-time-slider class="html-time-slider-basic__slider"></media-time-slider>
</media-container>
</video-player>
@@ -0,0 +1,2 @@
import '@videojs/html/video/player';
import '@videojs/html/ui/time-slider';
@@ -0,0 +1,10 @@
---
import HtmlDemo from '@/components/docs/demos/HtmlDemo.astro';
import html from './WithParts.html?raw';
import './WithParts.css';
---
<HtmlDemo html={html} />
<script>
import './WithParts.ts';
</script>
@@ -0,0 +1,95 @@
.html-time-slider-parts,
.html-time-slider-parts media-container {
display: block;
position: relative;
}
.html-time-slider-parts video {
width: 100%;
}
.html-time-slider-parts__slider {
position: absolute;
bottom: 0;
left: 0;
right: 0;
display: flex;
align-items: center;
height: 20px;
cursor: pointer;
}
.html-time-slider-parts__track {
position: absolute;
left: 0;
right: 0;
height: 4px;
background: rgba(255, 255, 255, 0.3);
border-radius: 9999px;
transition: height 150ms ease;
}
.html-time-slider-parts__slider[data-interactive] .html-time-slider-parts__track {
height: 6px;
}
.html-time-slider-parts__buffer {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: var(--media-slider-buffer);
background: rgba(255, 255, 255, 0.4);
border-radius: 9999px;
}
.html-time-slider-parts__fill {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: var(--media-slider-fill);
background: white;
border-radius: 9999px;
}
.html-time-slider-parts__thumb {
position: absolute;
left: var(--media-slider-fill);
width: 14px;
height: 14px;
background: white;
border-radius: 50%;
transform: translateX(-50%) scale(0);
transition: transform 150ms ease;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}
.html-time-slider-parts__slider[data-interactive] .html-time-slider-parts__thumb {
transform: translateX(-50%) scale(1);
}
.html-time-slider-parts__slider[data-dragging] .html-time-slider-parts__thumb {
transform: translateX(-50%) scale(1.1);
}
.html-time-slider-parts__value {
position: absolute;
left: var(--media-slider-pointer);
bottom: 100%;
transform: translateX(-50%);
margin-bottom: 6px;
background: rgba(0, 0, 0, 0.8);
color: white;
font-size: 12px;
padding: 2px 6px;
border-radius: 4px;
pointer-events: none;
white-space: nowrap;
opacity: 0;
transition: opacity 150ms ease;
}
.html-time-slider-parts__slider[data-pointing] .html-time-slider-parts__value {
opacity: 1;
}
@@ -0,0 +1,19 @@
<video-player class="html-time-slider-parts">
<media-container>
<video
src="https://stream.mux.com/lhnU49l1VGi3zrTAZhDm9LUUxSjpaPW9BL4jY25Kwo4/highest.mp4"
autoplay
muted
playsinline
loop
></video>
<media-time-slider class="html-time-slider-parts__slider">
<media-slider-track class="html-time-slider-parts__track">
<media-slider-buffer class="html-time-slider-parts__buffer"></media-slider-buffer>
<media-slider-fill class="html-time-slider-parts__fill"></media-slider-fill>
</media-slider-track>
<media-slider-thumb class="html-time-slider-parts__thumb"></media-slider-thumb>
<media-slider-value type="pointer" class="html-time-slider-parts__value"></media-slider-value>
</media-time-slider>
</media-container>
</video-player>
@@ -0,0 +1,2 @@
import '@videojs/html/video/player';
import '@videojs/html/ui/time-slider';
@@ -0,0 +1,28 @@
.react-time-slider-basic {
position: relative;
}
.react-time-slider-basic video {
width: 100%;
}
.react-time-slider-basic__slider {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 20px;
cursor: pointer;
background: rgba(255, 255, 255, 0.3);
}
.react-time-slider-basic__slider::before {
content: "";
position: absolute;
top: 0;
left: 0;
height: 100%;
width: var(--media-slider-fill);
background: white;
pointer-events: none;
}
@@ -0,0 +1,23 @@
import { createPlayer, TimeSlider } from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';
import './BasicUsage.css';
const Player = createPlayer({ features: videoFeatures });
export default function BasicUsage() {
return (
<Player.Provider>
<Player.Container className="react-time-slider-basic">
<Video
src="https://stream.mux.com/lhnU49l1VGi3zrTAZhDm9LUUxSjpaPW9BL4jY25Kwo4/highest.mp4"
autoPlay
muted
playsInline
loop
/>
<TimeSlider.Root className="react-time-slider-basic__slider" />
</Player.Container>
</Player.Provider>
);
}
@@ -0,0 +1,93 @@
.react-time-slider-parts {
position: relative;
}
.react-time-slider-parts video {
width: 100%;
}
.react-time-slider-parts__slider {
position: absolute;
bottom: 0;
left: 0;
right: 0;
display: flex;
align-items: center;
height: 20px;
cursor: pointer;
}
.react-time-slider-parts__track {
position: absolute;
left: 0;
right: 0;
height: 4px;
background: rgba(255, 255, 255, 0.3);
border-radius: 9999px;
transition: height 150ms ease;
}
.react-time-slider-parts__slider[data-interactive] .react-time-slider-parts__track {
height: 6px;
}
.react-time-slider-parts__buffer {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: var(--media-slider-buffer);
background: rgba(255, 255, 255, 0.4);
border-radius: 9999px;
}
.react-time-slider-parts__fill {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: var(--media-slider-fill);
background: white;
border-radius: 9999px;
}
.react-time-slider-parts__thumb {
position: absolute;
left: var(--media-slider-fill);
width: 14px;
height: 14px;
background: white;
border-radius: 50%;
transform: translateX(-50%) scale(0);
transition: transform 150ms ease;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}
.react-time-slider-parts__slider[data-interactive] .react-time-slider-parts__thumb {
transform: translateX(-50%) scale(1);
}
.react-time-slider-parts__slider[data-dragging] .react-time-slider-parts__thumb {
transform: translateX(-50%) scale(1.1);
}
.react-time-slider-parts__value {
position: absolute;
left: var(--media-slider-pointer);
bottom: 100%;
transform: translateX(-50%);
margin-bottom: 6px;
background: rgba(0, 0, 0, 0.8);
color: white;
font-size: 12px;
padding: 2px 6px;
border-radius: 4px;
pointer-events: none;
white-space: nowrap;
opacity: 0;
transition: opacity 150ms ease;
}
.react-time-slider-parts__slider[data-pointing] .react-time-slider-parts__value {
opacity: 1;
}
@@ -0,0 +1,30 @@
import { createPlayer, TimeSlider } from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';
import './WithParts.css';
const Player = createPlayer({ features: videoFeatures });
export default function WithParts() {
return (
<Player.Provider>
<Player.Container className="react-time-slider-parts">
<Video
src="https://stream.mux.com/lhnU49l1VGi3zrTAZhDm9LUUxSjpaPW9BL4jY25Kwo4/highest.mp4"
autoPlay
muted
playsInline
loop
/>
<TimeSlider.Root className="react-time-slider-parts__slider">
<TimeSlider.Track className="react-time-slider-parts__track">
<TimeSlider.Buffer className="react-time-slider-parts__buffer" />
<TimeSlider.Fill className="react-time-slider-parts__fill" />
</TimeSlider.Track>
<TimeSlider.Thumb className="react-time-slider-parts__thumb" />
<TimeSlider.Value type="pointer" className="react-time-slider-parts__value" />
</TimeSlider.Root>
</Player.Container>
</Player.Provider>
);
}
@@ -0,0 +1,10 @@
---
import HtmlDemo from '@/components/docs/demos/HtmlDemo.astro';
import html from './BasicUsage.html?raw';
import './BasicUsage.css';
---
<HtmlDemo html={html} />
<script>
import './BasicUsage.ts';
</script>
@@ -0,0 +1,60 @@
.html-volume-slider-basic,
.html-volume-slider-basic media-container {
display: block;
position: relative;
}
.html-volume-slider-basic video {
width: 100%;
}
.html-volume-slider-basic__mute-button {
position: absolute;
bottom: 10px;
left: 10px;
background: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(10px);
color: black;
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 9999px;
padding-block: 8px;
padding-inline: 20px;
cursor: pointer;
}
.html-volume-slider-basic__mute-button .show-when-muted {
display: none;
}
.html-volume-slider-basic__mute-button .show-when-unmuted {
display: none;
}
.html-volume-slider-basic__mute-button[data-muted] .show-when-muted {
display: inline;
}
.html-volume-slider-basic__mute-button:not([data-muted]) .show-when-unmuted {
display: inline;
}
.html-volume-slider-basic__slider {
position: absolute;
bottom: 10px;
right: 10px;
width: 100px;
height: 20px;
cursor: pointer;
background: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(10px);
border-radius: 9999px;
}
.html-volume-slider-basic__slider::before {
content: "";
position: absolute;
top: 0;
left: 0;
height: 100%;
width: var(--media-slider-fill);
background: white;
border-radius: 9999px;
pointer-events: none;
}
@@ -0,0 +1,16 @@
<video-player class="html-volume-slider-basic">
<media-container>
<video
src="https://stream.mux.com/lhnU49l1VGi3zrTAZhDm9LUUxSjpaPW9BL4jY25Kwo4/highest.mp4"
autoplay
muted
playsinline
loop
></video>
<media-mute-button class="html-volume-slider-basic__mute-button">
<span class="show-when-muted">Unmute</span>
<span class="show-when-unmuted">Mute</span>
</media-mute-button>
<media-volume-slider class="html-volume-slider-basic__slider"></media-volume-slider>
</media-container>
</video-player>
@@ -0,0 +1,3 @@
import '@videojs/html/video/player';
import '@videojs/html/ui/mute-button';
import '@videojs/html/ui/volume-slider';
@@ -0,0 +1,10 @@
---
import HtmlDemo from '@/components/docs/demos/HtmlDemo.astro';
import html from './WithParts.html?raw';
import './WithParts.css';
---
<HtmlDemo html={html} />
<script>
import './WithParts.ts';
</script>
@@ -0,0 +1,113 @@
.html-volume-slider-parts,
.html-volume-slider-parts media-container {
display: block;
position: relative;
}
.html-volume-slider-parts video {
width: 100%;
}
.html-volume-slider-parts__mute-button {
position: absolute;
bottom: 10px;
left: 10px;
background: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(10px);
color: black;
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 9999px;
padding-block: 8px;
padding-inline: 20px;
cursor: pointer;
}
.html-volume-slider-parts__mute-button .show-when-muted {
display: none;
}
.html-volume-slider-parts__mute-button .show-when-unmuted {
display: none;
}
.html-volume-slider-parts__mute-button[data-muted] .show-when-muted {
display: inline;
}
.html-volume-slider-parts__mute-button:not([data-muted]) .show-when-unmuted {
display: inline;
}
.html-volume-slider-parts__slider {
position: absolute;
bottom: 10px;
right: 10px;
width: 100px;
display: flex;
align-items: center;
height: 20px;
cursor: pointer;
}
.html-volume-slider-parts__track {
position: absolute;
left: 0;
right: 0;
height: 4px;
background: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(10px);
border-radius: 9999px;
transition: height 150ms ease;
}
.html-volume-slider-parts__slider[data-interactive] .html-volume-slider-parts__track {
height: 6px;
}
.html-volume-slider-parts__fill {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: var(--media-slider-fill);
background: white;
border-radius: 9999px;
}
.html-volume-slider-parts__thumb {
position: absolute;
left: var(--media-slider-fill);
width: 14px;
height: 14px;
background: white;
border-radius: 50%;
transform: translateX(-50%) scale(0);
transition: transform 150ms ease;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}
.html-volume-slider-parts__slider[data-interactive] .html-volume-slider-parts__thumb {
transform: translateX(-50%) scale(1);
}
.html-volume-slider-parts__slider[data-dragging] .html-volume-slider-parts__thumb {
transform: translateX(-50%) scale(1.1);
}
.html-volume-slider-parts__value {
position: absolute;
left: var(--media-slider-pointer);
bottom: 100%;
transform: translateX(-50%);
margin-bottom: 6px;
background: rgba(0, 0, 0, 0.8);
color: white;
font-size: 12px;
padding: 2px 6px;
border-radius: 4px;
pointer-events: none;
white-space: nowrap;
opacity: 0;
transition: opacity 150ms ease;
}
.html-volume-slider-parts__slider[data-pointing] .html-volume-slider-parts__value {
opacity: 1;
}
@@ -0,0 +1,22 @@
<video-player class="html-volume-slider-parts">
<media-container>
<video
src="https://stream.mux.com/lhnU49l1VGi3zrTAZhDm9LUUxSjpaPW9BL4jY25Kwo4/highest.mp4"
autoplay
muted
playsinline
loop
></video>
<media-mute-button class="html-volume-slider-parts__mute-button">
<span class="show-when-muted">Unmute</span>
<span class="show-when-unmuted">Mute</span>
</media-mute-button>
<media-volume-slider class="html-volume-slider-parts__slider">
<media-slider-track class="html-volume-slider-parts__track">
<media-slider-fill class="html-volume-slider-parts__fill"></media-slider-fill>
</media-slider-track>
<media-slider-thumb class="html-volume-slider-parts__thumb"></media-slider-thumb>
<media-slider-value type="pointer" class="html-volume-slider-parts__value"></media-slider-value>
</media-volume-slider>
</media-container>
</video-player>
@@ -0,0 +1,3 @@
import '@videojs/html/video/player';
import '@videojs/html/ui/mute-button';
import '@videojs/html/ui/volume-slider';
@@ -0,0 +1,45 @@
.react-volume-slider-basic {
position: relative;
}
.react-volume-slider-basic video {
width: 100%;
}
.react-volume-slider-basic__mute-button {
position: absolute;
bottom: 10px;
left: 10px;
background: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(10px);
color: black;
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 9999px;
padding-block: 8px;
padding-inline: 20px;
cursor: pointer;
}
.react-volume-slider-basic__slider {
position: absolute;
bottom: 10px;
right: 10px;
width: 100px;
height: 20px;
cursor: pointer;
background: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(10px);
border-radius: 9999px;
}
.react-volume-slider-basic__slider::before {
content: "";
position: absolute;
top: 0;
left: 0;
height: 100%;
width: var(--media-slider-fill);
background: white;
border-radius: 9999px;
pointer-events: none;
}
@@ -0,0 +1,27 @@
import { createPlayer, MuteButton, VolumeSlider } from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';
import './BasicUsage.css';
const Player = createPlayer({ features: videoFeatures });
export default function BasicUsage() {
return (
<Player.Provider>
<Player.Container className="react-volume-slider-basic">
<Video
src="https://stream.mux.com/lhnU49l1VGi3zrTAZhDm9LUUxSjpaPW9BL4jY25Kwo4/highest.mp4"
autoPlay
muted
playsInline
loop
/>
<MuteButton
className="react-volume-slider-basic__mute-button"
render={(props, state) => <button {...props}>{state.muted ? 'Unmute' : 'Mute'}</button>}
/>
<VolumeSlider.Root className="react-volume-slider-basic__slider" />
</Player.Container>
</Player.Provider>
);
}
@@ -0,0 +1,98 @@
.react-volume-slider-parts {
position: relative;
}
.react-volume-slider-parts video {
width: 100%;
}
.react-volume-slider-parts__mute-button {
position: absolute;
bottom: 10px;
left: 10px;
background: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(10px);
color: black;
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 9999px;
padding-block: 8px;
padding-inline: 20px;
cursor: pointer;
}
.react-volume-slider-parts__slider {
position: absolute;
bottom: 10px;
right: 10px;
width: 100px;
display: flex;
align-items: center;
height: 20px;
cursor: pointer;
}
.react-volume-slider-parts__track {
position: absolute;
left: 0;
right: 0;
height: 4px;
background: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(10px);
border-radius: 9999px;
transition: height 150ms ease;
}
.react-volume-slider-parts__slider[data-interactive] .react-volume-slider-parts__track {
height: 6px;
}
.react-volume-slider-parts__fill {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: var(--media-slider-fill);
background: white;
border-radius: 9999px;
}
.react-volume-slider-parts__thumb {
position: absolute;
left: var(--media-slider-fill);
width: 14px;
height: 14px;
background: white;
border-radius: 50%;
transform: translateX(-50%) scale(0);
transition: transform 150ms ease;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}
.react-volume-slider-parts__slider[data-interactive] .react-volume-slider-parts__thumb {
transform: translateX(-50%) scale(1);
}
.react-volume-slider-parts__slider[data-dragging] .react-volume-slider-parts__thumb {
transform: translateX(-50%) scale(1.1);
}
.react-volume-slider-parts__value {
position: absolute;
left: var(--media-slider-pointer);
bottom: 100%;
transform: translateX(-50%);
margin-bottom: 6px;
background: rgba(0, 0, 0, 0.8);
color: white;
font-size: 12px;
padding: 2px 6px;
border-radius: 4px;
pointer-events: none;
white-space: nowrap;
opacity: 0;
transition: opacity 150ms ease;
}
.react-volume-slider-parts__slider[data-pointing] .react-volume-slider-parts__value {
opacity: 1;
}
@@ -0,0 +1,33 @@
import { createPlayer, MuteButton, VolumeSlider } from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';
import './WithParts.css';
const Player = createPlayer({ features: videoFeatures });
export default function WithParts() {
return (
<Player.Provider>
<Player.Container className="react-volume-slider-parts">
<Video
src="https://stream.mux.com/lhnU49l1VGi3zrTAZhDm9LUUxSjpaPW9BL4jY25Kwo4/highest.mp4"
autoPlay
muted
playsInline
loop
/>
<MuteButton
className="react-volume-slider-parts__mute-button"
render={(props, state) => <button {...props}>{state.muted ? 'Unmute' : 'Mute'}</button>}
/>
<VolumeSlider.Root className="react-volume-slider-parts__slider">
<VolumeSlider.Track className="react-volume-slider-parts__track">
<VolumeSlider.Fill className="react-volume-slider-parts__fill" />
</VolumeSlider.Track>
<VolumeSlider.Thumb className="react-volume-slider-parts__thumb" />
<VolumeSlider.Value type="pointer" className="react-volume-slider-parts__value" />
</VolumeSlider.Root>
</Player.Container>
</Player.Provider>
);
}