routie dev init since i didn't adhere to any proper guidance up until now

This commit is contained in:
2026-04-29 22:27:29 -06:00
commit e1dabb71e2
15301 changed files with 3562618 additions and 0 deletions
+134
View File
@@ -0,0 +1,134 @@
@layer vuetify-components {
.v-video {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
--v-video-aspect-ratio: 1.7777777778;
--v-video-controls-height: 58px;
--v-video-controls-pill-border-radius: 99px;
}
.v-video--density-default {
--v-video-controls-height: 58px;
}
.v-video--density-comfortable {
--v-video-controls-height: 50px;
}
.v-video--density-compact {
--v-video-controls-height: 42px;
}
.v-video__video {
position: absolute;
width: 100%;
height: 100%;
object-fit: cover;
z-index: 0;
}
.v-video__video::-webkit-media-controls {
display: none !important;
}
.v-video__video ~ * {
z-index: 1;
}
.v-video__header {
position: relative;
opacity: 0;
transition: opacity 0.6s ease-in-out;
pointer-events: none;
}
.v-video__header > * {
pointer-events: auto;
}
.v-video__content {
position: relative;
border-radius: inherit;
z-index: 0;
}
.v-video__content {
box-shadow: 0px 1px 2px 0px rgba(var(--v-shadow-color), var(--v-shadow-key-opacity, 0.3)), 0px 2px 6px 2px rgba(var(--v-shadow-color), var(--v-shadow-ambient-opacity, 0.15));
--v-elevation-overlay: color-mix(in srgb, var(--v-elevation-overlay-color) 4%, transparent);
}
.v-video:not(.v-video--idle):not(.v-video--error) .v-video__content .v-video__overlay-fill, .v-video:not(.v-video--idle):not(.v-video--error) .v-video__content .v-video__overlay-fill > * {
pointer-events: none;
}
.v-video:not(.v-video--error) .v-video__content {
cursor: pointer;
}
.v-video__overlay-fill {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
border-radius: inherit;
}
.v-video__overlay-fill > .v-img {
position: absolute;
border-radius: inherit;
inset: 0;
}
.v-video:has(.v-video-controls:not(.v-video-controls--detached)) .v-video__content .v-video__overlay-fill {
padding-bottom: var(--v-video-controls-height);
}
.v-video__center-icon.v-icon-btn {
border: 5px solid currentColor;
transition-property: transform, opacity;
transition-duration: 0.28s;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}
.v-video__center-icon.v-icon-btn.v-video__center-icon--play {
opacity: 0.6;
}
.v-video__content:hover .v-video__center-icon--play {
transform: scale(1.05);
opacity: 1;
}
.v-video--variant-player {
width: 100%;
}
.v-video--variant-player > .v-video__content {
width: 100%;
aspect-ratio: var(--v-video-aspect-ratio);
}
.v-video--variant-background {
outline: none;
position: absolute;
inset: 0;
pointer-events: none;
}
.v-video--variant-background > .v-video__content {
width: 100%;
height: 100%;
}
.v-video .v-video-controls {
opacity: 0;
}
.v-video .v-video-controls:not(.v-video-controls--detached) {
margin-top: calc(-1 * var(--v-video-controls-height));
}
.v-video .v-video-controls--detached {
opacity: 1;
}
.v-video .v-video-controls--floating:not(.v-video-controls--detached) {
margin-top: calc(-1 * var(--v-video-controls-height) - 12px);
margin-bottom: 12px;
}
.v-video:not(.v-video--playing) .v-video__header,
.v-video:not(.v-video--playing) .v-video-controls, .v-video:hover .v-video__header,
.v-video:hover .v-video-controls {
opacity: 1;
}
.v-video--rounded {
border-radius: 4px;
}
.v-video:fullscreen .v-video__content {
min-height: 100% !important;
min-width: 100% !important;
}
.poster-fade-out-leave-active {
transition: opacity 1s linear 0.3s;
}
.poster-fade-out-leave-to {
opacity: 0;
}
}
File diff suppressed because it is too large Load Diff
+474
View File
@@ -0,0 +1,474 @@
import { createVNode as _createVNode, normalizeClass as _normalizeClass, createElementVNode as _createElementVNode, mergeProps as _mergeProps, normalizeStyle as _normalizeStyle } from "vue";
// Styles
import "./VVideo.css";
// Components
import { makeVVideoControlsProps, VVideoControls } from "./VVideoControls.js";
import { VFadeTransition } from "../../components/transitions/index.js";
import { VDefaultsProvider } from "../../components/VDefaultsProvider/index.js";
import { VIcon } from "../../components/VIcon/index.js";
import { VImg } from "../../components/VImg/VImg.js";
import { VProgressCircular } from "../../components/VProgressCircular/VProgressCircular.js";
import { VIconBtn } from "../VIconBtn/VIconBtn.js"; // Composables
import { useDisplay } from "../../composables/index.js";
import { makeComponentProps } from "../../composables/component.js";
import { makeDensityProps, useDensity } from "../../composables/density.js";
import { makeDimensionProps, useDimension } from "../../composables/dimensions.js";
import { useElevation } from "../../composables/elevation.js";
import { forwardRefs } from "../../composables/forwardRefs.js";
import { useProxiedModel } from "../../composables/proxiedModel.js";
import { useRounded } from "../../composables/rounded.js";
import { makeThemeProps, provideTheme } from "../../composables/theme.js";
import { MaybeTransition } from "../../composables/transition.js"; // Utilities
import { nextTick, onBeforeUnmount, onMounted, ref, shallowRef, toRef, Transition, watch } from 'vue';
import { createRange, genericComponent, omit, pick, propsFactory, useRender } from "../../util/index.js"; // Types
const allowedVariants = ['background', 'player'];
export const makeVVideoProps = propsFactory({
aspectRatio: [String, Number],
autoplay: Boolean,
muted: Boolean,
eager: Boolean,
error: [Object, Boolean],
src: String,
srcObject: Object,
type: String,
// e.g. video/mp4
image: String,
hideOverlay: Boolean,
noFullscreen: Boolean,
startAt: [Number, String],
variant: {
type: String,
default: 'player',
validator: v => allowedVariants.includes(v)
},
controlsTransition: {
type: [Boolean, String, Object],
component: VFadeTransition
},
controlsVariant: {
type: String,
default: 'default'
},
controlsProps: {
type: Object
},
rounded: [Boolean, Number, String, Array],
...makeComponentProps(),
...makeDensityProps(),
...makeDimensionProps(),
...makeThemeProps(),
...omit(makeVVideoControlsProps(), ['fullscreen', 'variant'])
}, 'VVideo');
export const VVideo = genericComponent()({
name: 'VVideo',
inheritAttrs: false,
props: makeVVideoProps(),
emits: {
error: val => true,
loaded: element => true,
'update:error': val => true,
'update:playing': val => true,
'update:progress': val => true,
'update:volume': val => true
},
setup(props, {
attrs,
emit,
slots
}) {
const {
themeClasses
} = provideTheme(props);
const {
densityClasses
} = useDensity(props);
const {
dimensionStyles
} = useDimension(props);
const {
elevationClasses
} = useElevation(props);
const {
ssr
} = useDisplay();
const roundedForContainer = toRef(() => Array.isArray(props.rounded) ? props.rounded[0] : props.rounded);
const roundedForControls = toRef(() => Array.isArray(props.rounded) ? props.rounded.at(-1) : props.rounded ?? false);
const {
roundedClasses: roundedContainerClasses
} = useRounded(roundedForContainer);
const {
roundedClasses: roundedControlsClasses
} = useRounded(roundedForControls);
const containerRef = ref();
const videoRef = ref();
const controlsRef = ref();
const playing = useProxiedModel(props, 'playing');
const progress = useProxiedModel(props, 'progress');
const volume = useProxiedModel(props, 'volume', 0, v => Number(v ?? 0));
const fullscreen = shallowRef(false);
const waiting = shallowRef(false);
const triggered = shallowRef(false);
const startAfterLoad = shallowRef(false);
const error = useProxiedModel(props, 'error');
const state = shallowRef(props.autoplay ? 'loading' : 'idle');
const duration = shallowRef(0);
const fullscreenEnabled = toRef(() => !props.noFullscreen && !String(attrs.controlsList ?? '').includes('nofullscreen'));
function onTimeupdate() {
const {
currentTime,
duration
} = videoRef.value;
progress.value = duration === 0 ? 0 : 100 * currentTime / duration;
}
async function onTriggered() {
await nextTick();
if (!videoRef.value) return;
videoRef.value.addEventListener('timeupdate', onTimeupdate);
videoRef.value.volume = volume.value / 100;
if (state.value !== 'loaded') {
state.value = 'loading';
}
}
function onVideoLoaded() {
state.value = 'loaded';
duration.value = videoRef.value.duration;
const startTime = Number(props.startAt ?? 0);
if (startTime && startTime <= duration.value) {
videoRef.value.currentTime = startTime;
progress.value = duration.value === 0 ? 0 : 100 * startTime / duration.value;
}
if (startAfterLoad.value) {
setTimeout(() => playing.value = true, 100);
}
emit('loaded', videoRef.value);
}
function onVideoError(e) {
state.value = 'error';
error.value = videoRef.value.error;
}
watch(error, v => {
if (v && state.value !== 'error') {
videoRef.value?.pause();
state.value = 'error';
}
}, {
immediate: true
});
function retry() {
if (state.value !== 'error') return;
error.value = false;
state.value = 'loading';
triggered.value = true;
videoRef.value?.load();
if (!props.srcObject) {
videoRef.value?.play();
}
}
function onClick() {
if (['loaded', 'error'].includes(state.value)) return;
triggered.value = true;
startAfterLoad.value = !startAfterLoad.value;
}
function onKeydown(e) {
if (!videoRef.value || e.ctrlKey) return;
if (e.key.startsWith('Arrow')) {
e.preventDefault();
}
switch (true) {
case e.key === ' ':
{
if (!['A', 'BUTTON'].includes(e.target?.tagName)) {
e.preventDefault();
playing.value = !playing.value;
}
break;
}
case e.key === 'ArrowRight':
{
const step = 10 * (e.shiftKey ? 6 : 1);
videoRef.value.currentTime = Math.min(videoRef.value.currentTime + step, duration.value);
// TODO: show skip indicator
break;
}
case e.key === 'ArrowLeft':
{
const step = 10 * (e.shiftKey ? 6 : 1);
videoRef.value.currentTime = Math.max(videoRef.value.currentTime - step, 0);
// TODO: show skip indicator
break;
}
case createRange(10).map(String).includes(e.key):
{
skipTo(Number(e.key) * 10);
break;
}
case e.key === 'ArrowUp':
{
volume.value = Math.min(volume.value + 10, 100);
// TODO: show volume change indicator
break;
}
case e.key === 'ArrowDown':
{
volume.value = Math.max(volume.value - 10, 0);
// TODO: show volume change indicator
break;
}
case e.key === 'm':
{
controlsRef.value?.toggleMuted();
break;
}
case e.key === 'f':
{
toggleFullscreen();
break;
}
}
}
function skipTo(v) {
if (!videoRef.value) return;
progress.value = v;
videoRef.value.currentTime = duration.value * v / 100;
}
watch(() => props.src, v => {
progress.value = 0;
});
watch(() => props.srcObject, async v => {
if (v) triggered.value = true;
await nextTick();
if (videoRef.value) videoRef.value.srcObject = v ?? null;
});
watch(videoRef, v => {
if (v && props.srcObject) v.srcObject = props.srcObject;
});
watch(playing, v => {
if (!videoRef.value) return;
if (v) {
videoRef.value.play();
} else {
videoRef.value.pause();
}
});
watch(volume, v => {
if (!videoRef.value) return;
videoRef.value.volume = v / 100;
});
watch(triggered, () => onTriggered(), {
once: true
});
watch(() => props.eager, v => v && (triggered.value = true), {
immediate: true
});
onMounted(() => {
if (props.autoplay && !ssr) {
triggered.value = true;
startAfterLoad.value = true;
}
});
onBeforeUnmount(() => {
videoRef.value?.removeEventListener('timeupdate', onTimeupdate);
document.body.removeEventListener('keydown', fullscreenExitShortcut);
document.removeEventListener('fullscreenchange', onFullscreenExit);
});
function focusSlider() {
const container = videoRef.value?.closest('.v-video');
const innerSlider = container?.querySelector('[role="slider"]');
innerSlider?.focus();
}
function fullscreenExitShortcut(e) {
if (['ESC', 'f'].includes(e.key)) {
toggleFullscreen();
document.body.removeEventListener('keydown', fullscreenExitShortcut);
}
}
async function toggleFullscreen() {
if (!fullscreenEnabled.value || !document.fullscreenEnabled) {
return;
}
if (document.fullscreenElement) {
document.exitFullscreen();
onFullscreenExit();
} else {
await containerRef.value?.requestFullscreen();
document.body.addEventListener('keydown', fullscreenExitShortcut);
document.addEventListener('fullscreenchange', onFullscreenExit);
fullscreen.value = true;
}
}
function onFullscreenExit() {
// event fires with a delay after requestFullscreen(), ignore first run
if (document.fullscreenElement) return;
focusSlider();
fullscreen.value = false;
document.body.removeEventListener('keydown', fullscreenExitShortcut);
document.removeEventListener('fullscreenchange', onFullscreenExit);
}
function onVideoClick(e) {
e.preventDefault();
if (state.value === 'loaded') {
playing.value = !playing.value;
focusSlider();
}
}
function onDoubleClick(e) {
e.preventDefault();
toggleFullscreen();
}
let lastTap = 0;
function onTouchend(e) {
const now = performance.now();
if (now - lastTap < 500) {
e.preventDefault();
toggleFullscreen();
} else {
lastTap = now;
}
}
useRender(() => {
const showControls = state.value === 'loaded' && props.variant === 'player' && props.controlsVariant !== 'hidden';
const posterTransition = props.variant === 'background' ? 'poster-fade-out' : 'fade-transition';
const controlsProps = {
...VVideoControls.filterProps(omit(props, ['variant', 'rounded', 'hideVolume'])),
rounded: Array.isArray(props.rounded) ? props.rounded.at(-1) : props.rounded,
fullscreen: fullscreen.value,
hideVolume: props.hideVolume || props.muted,
hideFullscreen: props.hideFullscreen || !fullscreenEnabled.value,
density: props.density,
variant: props.controlsVariant,
playing: playing.value,
progress: progress.value,
duration: duration.value,
volume: volume.value,
...props.controlsProps
};
const controlsEventHandlers = {
onSkip: v => skipTo(v),
'onClick:fullscreen': () => toggleFullscreen(),
'onUpdate:playing': v => playing.value = v,
'onUpdate:progress': v => skipTo(v),
'onUpdate:volume': v => volume.value = v,
onClick: e => e.stopPropagation()
};
const controlslist = [attrs.controlslist, props.noFullscreen ? 'nofullscreen' : ''].filter(Boolean).join(' ');
const loadingIndicator = _createVNode(VProgressCircular, {
"indeterminate": true,
"color": props.color,
"width": "3",
"size": Math.min(100, Number(props.height) / 2 || 50)
}, null);
const overlayPlayIcon = _createVNode(VIconBtn, {
"icon": "$play",
"size": "80",
"color": "#fff",
"variant": "outlined",
"iconSize": "50",
"class": _normalizeClass(['v-video__center-icon', 'v-video__center-icon--play']),
"onClick": onVideoClick
}, null);
const errorIconProps = {
icon: '$warning',
size: '70'
};
const activeOverlays = {
playIcon: props.variant === 'player' && state.value === 'loaded' && !props.hideOverlay && !playing.value,
poster: state.value !== 'loaded' && state.value !== 'error',
loading: props.variant === 'player' && state.value !== 'error' && (state.value === 'loading' || waiting.value),
error: props.variant === 'player' && state.value === 'error'
};
return _createElementVNode("div", {
"ref": containerRef,
"class": _normalizeClass(['v-video', `v-video--variant-${props.variant}`, `v-video--${state.value}`, {
'v-video--playing': playing.value
}, themeClasses.value, densityClasses.value, roundedContainerClasses.value, props.class]),
"style": _normalizeStyle([{
'--v-video-aspect-ratio': props.aspectRatio
}, props.variant === 'background' ? [] : pick(dimensionStyles.value, ['width', 'minWidth', 'maxWidth']), props.style]),
"onKeydown": onKeydown,
"onClick": onClick
}, [_createElementVNode("div", {
"class": _normalizeClass(['v-video__content', elevationClasses.value]),
"style": _normalizeStyle([props.variant === 'background' ? [] : dimensionStyles.value])
}, [(props.eager || triggered.value) && _createElementVNode("video", _mergeProps({
"key": "video-element",
"class": ['v-video__video', roundedContainerClasses.value]
}, omit(attrs, ['controlslist', 'class', 'style']), {
"controlslist": controlslist,
"autoplay": props.autoplay,
"muted": props.muted,
"playsinline": true,
"ref": videoRef,
"onLoadeddata": onVideoLoaded,
"onError": onVideoError,
"onPlay": () => playing.value = true,
"onPause": () => playing.value = false,
"onWaiting": () => waiting.value = true,
"onPlaying": () => waiting.value = false,
"onClick": onVideoClick,
"onDblclick": onDoubleClick,
"onTouchend": onTouchend
}), [slots.sources?.() ?? _createElementVNode("source", {
"src": props.src,
"type": props.type
}, null)]), _createVNode(Transition, {
"name": "fade-transition"
}, {
default: () => [activeOverlays.playIcon && _createElementVNode("div", {
"class": "v-video__overlay-fill"
}, [overlayPlayIcon])]
}), props.variant === 'player' && !!slots.header && _createElementVNode("div", {
"key": "header",
"class": "v-video__header"
}, [slots.header()]), _createVNode(MaybeTransition, {
"transition": posterTransition
}, {
default: () => [activeOverlays.poster && _createElementVNode("div", {
"class": "v-video__overlay-fill"
}, [_createVNode(VImg, {
"cover": true,
"src": props.image
}, {
default: () => [_createElementVNode("div", {
"class": _normalizeClass(['v-video__overlay-fill', ...roundedContainerClasses.value])
}, [props.variant === 'player' && overlayPlayIcon])]
})])]
}), activeOverlays.loading && _createElementVNode("div", {
"key": "loading-overlay",
"class": "v-video__overlay-fill"
}, [loadingIndicator]), activeOverlays.error && _createElementVNode("div", {
"key": "error-overlay",
"class": "v-video__overlay-fill"
}, [slots.error ? _createVNode(VDefaultsProvider, {
"defaults": {
VIcon: errorIconProps
}
}, {
default: () => [slots.error?.({
error: error.value
})]
}) : _createVNode(VIcon, errorIconProps, null)])]), _createVNode(MaybeTransition, {
"key": "actions",
"transition": props.controlsTransition
}, {
default: () => [showControls && _createVNode(VVideoControls, _mergeProps({
"ref": controlsRef,
"class": roundedControlsClasses.value
}, controlsProps, controlsEventHandlers), {
default: slots.controls,
prepend: slots.prepend,
append: slots.append
})]
})]);
});
return {
video: videoRef,
...forwardRefs({
retry,
skipTo,
toggleFullscreen
}, controlsRef)
};
}
});
//# sourceMappingURL=VVideo.js.map
File diff suppressed because one or more lines are too long
+134
View File
@@ -0,0 +1,134 @@
@use '../../styles/settings'
@use '../../styles/tools'
@use './variables' as *
@include tools.layer('components')
.v-video
display: flex
flex-direction: column
align-items: center
position: relative
--v-video-aspect-ratio: #{$video-aspect-ratio}
--v-video-controls-height: #{$video-controls-height}
--v-video-controls-pill-border-radius: #{$video-controls-pill-border-radius}
@at-root
@include tools.density('v-video', $video-density) using ($modifier)
--v-video-controls-height: #{$video-controls-height + $modifier * 2}
&__video
position: absolute
width: 100%
height: 100%
object-fit: cover
z-index: 0
&::-webkit-media-controls
display: none !important
& ~ *
z-index: 1
&__header
position: relative
opacity: 0
transition: opacity .6s ease-in-out
pointer-events: none
> *
pointer-events: auto
&__content
position: relative
border-radius: inherit
z-index: 0
@include tools.elevation($video-elevation)
&:not(.v-video--idle):not(.v-video--error)
.v-video__content .v-video__overlay-fill
&, > *
pointer-events: none
&:not(.v-video--error)
.v-video__content
cursor: pointer
&__overlay-fill
position: absolute
inset: 0
display: flex
align-items: center
justify-content: center
border-radius: inherit
> .v-img
position: absolute
border-radius: inherit
inset: 0
&:has(.v-video-controls:not(.v-video-controls--detached))
.v-video__content .v-video__overlay-fill
padding-bottom: var(--v-video-controls-height) // controls size
&__center-icon.v-icon-btn
border: $video-center-icon-border
transition-property: transform, opacity
transition-duration: 0.28s
transition-timing-function: settings.$standard-easing
&.v-video__center-icon--play
opacity: $video-center-icon-opacity
&__content:hover &__center-icon--play
transform: $video-center-icon-hover-transform
opacity: 1
&--variant-player
width: $video-player-width
> .v-video__content
width: 100%
aspect-ratio: var(--v-video-aspect-ratio)
&--variant-background
outline: none
position: absolute
inset: 0
pointer-events: none
> .v-video__content
width: 100%
height: 100%
// Integration with VVideoControls: pull controls up to overlap the video,
// and hide them by default until hover / pause.
.v-video-controls
opacity: 0
&:not(.v-video-controls--detached)
margin-top: calc(-1 * var(--v-video-controls-height))
&--detached
opacity: 1
&--floating:not(.v-video-controls--detached)
margin-top: calc(-1 * var(--v-video-controls-height) - #{$video-controls-floating-margin})
margin-bottom: $video-controls-floating-margin
&:not(.v-video--playing),
&:hover
.v-video__header,
.v-video-controls
opacity: 1
&--rounded
border-radius: $video-rounded-border-radius
&:fullscreen .v-video__content
min-height: 100% !important
min-width: 100% !important
.poster-fade-out
&-leave-active
transition: opacity 1s linear 0.3s
&-leave-to
opacity: 0
+154
View File
@@ -0,0 +1,154 @@
@layer vuetify-components {
.v-video-controls {
--v-video-controls-height: 58px;
--v-video-controls-pill-border-radius: 99px;
--v-video-controls-gap: 16px;
--v-background-opacity: 0.8;
--v-video-controls-pill-height: 42px;
flex: 1 0 100%;
align-self: stretch;
padding: 8px;
display: flex;
align-items: center;
position: relative;
gap: var(--v-video-controls-gap);
min-height: var(--v-video-controls-height);
backdrop-filter: blur(5px);
background-color: rgba(var(--v-theme-surface), var(--v-background-opacity));
color: rgb(var(--v-theme-on-surface));
width: 100%;
opacity: 1;
}
.v-video-controls--density-default {
--v-video-controls-gap: 16px;
--v-video-controls-height: 58px;
}
.v-video-controls--density-comfortable {
--v-video-controls-gap: 12px;
--v-video-controls-height: 50px;
}
.v-video-controls--density-compact {
--v-video-controls-gap: 8px;
--v-video-controls-height: 42px;
}
.v-video-controls, .v-video-controls--pills > .v-video-control__pill {
transition: 0.6s ease-in-out;
transition-property: opacity, background-color;
}
.v-video-controls--pills > .v-video-control__pill {
box-shadow: 0px 1px 2px 0px rgba(var(--v-shadow-color), var(--v-shadow-key-opacity, 0.3)), 0px 2px 6px 2px rgba(var(--v-shadow-color), var(--v-shadow-ambient-opacity, 0.15));
--v-elevation-overlay: color-mix(in srgb, var(--v-elevation-overlay-color) 4%, transparent);
}
.v-video-controls:hover {
--v-background-opacity: 1;
}
.v-video-controls .v-video-control__pill {
display: flex;
align-items: center;
gap: 8px;
}
.v-video-controls--pills {
--v-background-opacity: 1;
--v-video-controls-height: calc(var(--v-video-controls-pill-height) + 24px);
backdrop-filter: none;
pointer-events: none;
}
.v-video-controls--pills > * {
pointer-events: auto;
}
.v-video-controls--pills:not(:empty) {
background: transparent;
}
.v-video-controls--pills > .v-video-control__pill {
backdrop-filter: blur(5px);
background: rgba(var(--v-theme-surface), var(--v-background-opacity));
border-radius: var(--v-video-controls-pill-border-radius);
min-height: var(--v-video-controls-pill-height);
min-width: var(--v-video-controls-pill-height);
padding: 0 6px;
z-index: 1;
}
.v-video-controls--pills > .v-video-control__pill > .v-icon-btn {
border-radius: inherit;
}
.v-video-controls--pills > .v-video-control__pill:empty {
display: none;
}
.v-video-controls--pills > .v-video-control__pill:has(> *:only-child) {
padding-inline: 0;
justify-content: center;
border-radius: var(--v-video-controls-pill-border-radius);
}
.v-video-controls--pills > .v-video__time {
padding: 0 16px;
}
.v-video-controls--variant-tube .v-slider.v-video__track {
position: absolute;
top: -8px;
left: 0;
right: 0;
}
.v-video-controls--variant-tube .v-slider.v-video__track .v-slider-track {
height: calc(var(--v-slider-track-size) + 10px);
}
.v-video-controls--variant-tube .v-slider.v-video__track .v-slider-track__fill {
height: var(--v-slider-track-size);
}
.v-video-controls--split-time {
padding-inline: 20px;
}
.v-video-controls:not(.v-video-controls--floating) {
border-bottom-left-radius: inherit;
border-bottom-right-radius: inherit;
}
.v-video-controls:not(.v-video-controls--floating):not(.v-video-controls--pills) {
border-top: thin solid color-mix(in srgb, rgb(var(--v-theme-surface-variant)) 20%, transparent);
}
.v-video-controls--floating {
--v-background-opacity: 1;
border-radius: inherit;
margin-inline: 12px;
width: calc(100% - 2 * 12px);
}
.v-video-controls--detached {
--v-background-opacity: 1;
position: relative;
margin-block-start: 12px;
padding-inline: 12px;
backdrop-filter: none;
border-top: none;
border-radius: inherit;
opacity: 1;
}
.v-video-controls--detached:not(.v-video-controls--pills) {
box-shadow: 0px 1px 2px 0px rgba(var(--v-shadow-color), var(--v-shadow-key-opacity, 0.3)), 0px 2px 6px 2px rgba(var(--v-shadow-color), var(--v-shadow-ambient-opacity, 0.15));
--v-elevation-overlay: color-mix(in srgb, var(--v-elevation-overlay-color) 4%, transparent);
}
.v-video-controls--fullscreen {
position: absolute;
bottom: 0;
z-index: 2147483647;
}
.v-video-controls--fullscreen.v-video-controls--floating {
bottom: 12px;
}
.v-video__track {
position: relative;
z-index: 1;
}
.v-video__track.v-slider.v-input--horizontal > .v-input__control {
min-height: 4px;
}
.v-video__track .v-slider-thumb:not(:hover) .v-slider-thumb__label {
opacity: 0;
transition-delay: 0.2s;
}
.v-video__track .v-slider-thumb__label {
color: rgb(var(--v-theme-on-surface-variant)) !important;
}
.v-video__time {
font-size: 0.875rem;
line-height: 1;
letter-spacing: 0.02em;
}
}
File diff suppressed because it is too large Load Diff
+259
View File
@@ -0,0 +1,259 @@
import { Fragment as _Fragment, createVNode as _createVNode, withDirectives as _withDirectives, normalizeClass as _normalizeClass, normalizeStyle as _normalizeStyle, createElementVNode as _createElementVNode, createTextVNode as _createTextVNode, mergeProps as _mergeProps } from "vue";
/* eslint-disable complexity */
// Styles
import "./VVideoControls.css";
// Components
import { VVideoVolume } from "./VVideoVolume.js";
import { VDefaultsProvider } from "../../components/VDefaultsProvider/VDefaultsProvider.js";
import { VSpacer } from "../../components/VGrid/VSpacer.js";
import { VSlider } from "../../components/VSlider/VSlider.js";
import { VIconBtn } from "../VIconBtn/VIconBtn.js"; // Composables
import { useBackgroundColor } from "../../composables/color.js";
import { makeDensityProps, useDensity } from "../../composables/density.js";
import { makeElevationProps, useElevation } from "../../composables/elevation.js";
import { useLocale } from "../../composables/locale.js";
import { useProxiedModel } from "../../composables/proxiedModel.js";
import { makeThemeProps, provideTheme } from "../../composables/theme.js"; // Directives
import vTooltip from "../../directives/tooltip/index.js"; // Utilities
import { computed, shallowRef, toRef } from 'vue';
import { formatTime, genericComponent, propsFactory, useRender } from "../../util/index.js"; // Types
const allowedVariants = ['hidden', 'default', 'tube', 'mini'];
export const makeVVideoControlsProps = propsFactory({
color: String,
backgroundColor: String,
trackColor: String,
playing: Boolean,
hidePlay: Boolean,
hideVolume: Boolean,
hideFullscreen: Boolean,
hideProgressBar: Boolean,
fullscreen: Boolean,
floating: Boolean,
splitTime: Boolean,
pills: Boolean,
detached: Boolean,
progress: {
type: Number,
default: 0
},
duration: {
type: Number,
default: 0
},
volume: [Number, String],
variant: {
type: String,
default: 'default',
validator: v => allowedVariants.includes(v)
},
volumeProps: Object,
...makeDensityProps(),
...makeElevationProps(),
...makeThemeProps()
}, 'VVideoControls');
export const VVideoControls = genericComponent()({
name: 'VVideoControls',
directives: {
vTooltip: vTooltip
},
props: makeVVideoControlsProps(),
emits: {
'update:playing': val => true,
'update:progress': val => true,
'update:volume': val => true,
skip: val => true,
'click:fullscreen': () => true
},
setup(props, {
emit,
slots
}) {
const {
t
} = useLocale();
const {
themeClasses,
current: currentTheme
} = provideTheme(props);
const {
densityClasses
} = useDensity(props);
const {
elevationClasses
} = useElevation(props);
const {
backgroundColorClasses,
backgroundColorStyles
} = useBackgroundColor(() => {
const fallbackBackground = props.detached ? 'surface' : undefined;
return props.backgroundColor ?? fallbackBackground;
});
const trackColor = toRef(() => {
if (props.trackColor) {
return props.trackColor;
}
const fallback = currentTheme.value.dark || !props.pills ? undefined : 'surface';
return (props.pills ? props.backgroundColor : props.color) ?? fallback;
});
const playing = useProxiedModel(props, 'playing');
const progress = useProxiedModel(props, 'progress');
const volume = useProxiedModel(props, 'volume', 0, v => Number(v ?? 0));
const lastVolume = shallowRef();
const currentTime = computed(() => {
const secondsElapsed = Math.round(props.progress / 100 * props.duration);
return {
elapsed: formatTime(secondsElapsed),
remaining: formatTime(props.duration - secondsElapsed),
total: formatTime(props.duration)
};
});
const labels = computed(() => {
const playIconLocaleKey = playing.value ? 'pause' : 'play';
const volumeIconLocaleKey = props.volumeProps?.inline ? volume.value ? 'mute' : 'unmute' : 'showVolume';
const fullscreenIconLocaleKey = props.fullscreen ? 'exitFullscreen' : 'enterFullscreen';
return {
seek: t('$vuetify.video.seek'),
volume: t('$vuetify.video.volume'),
playAction: t(`$vuetify.video.${playIconLocaleKey}`),
volumeAction: t(`$vuetify.video.${volumeIconLocaleKey}`),
fullscreenAction: t(`$vuetify.video.${fullscreenIconLocaleKey}`)
};
});
function play() {
playing.value = true;
}
function pause() {
playing.value = false;
}
function skipTo(v) {
progress.value = v;
}
function toggleMuted() {
if (volume.value) {
lastVolume.value = volume.value;
volume.value = 0;
} else {
volume.value = lastVolume.value ?? 100;
}
}
function toggleFullscreen() {
emit('click:fullscreen');
}
useRender(() => {
const sizes = props.pills ? [42, 36, 30] : [32, 28, 24];
const innerDefaults = {
VIconBtn: {
size: props.density === 'compact' ? sizes[2] : props.density === 'comfortable' ? sizes[1] : sizes[0],
iconSize: props.density === 'compact' ? 20 : props.density === 'comfortable' ? 24 : 26,
variant: 'text',
color: props.color
},
VSlider: {
thumbSize: props.variant === 'tube' ? 10 : 16,
hideDetails: true
}
};
const regularBtnSize = innerDefaults.VIconBtn.size;
const playBtnSize = props.pills ? regularBtnSize + 8 : regularBtnSize;
const pillClasses = ['v-video-control__pill', props.pills ? elevationClasses.value : [], props.pills ? backgroundColorClasses.value : []];
const pillStyles = props.pills ? backgroundColorStyles.value : [];
const slotProps = {
play,
pause,
playing: playing.value,
progress: progress.value,
currentTime: currentTime.value,
skipTo,
volume,
toggleMuted,
fullscreen: props.fullscreen,
toggleFullscreen,
labels: labels.value
};
return _createElementVNode("div", {
"class": _normalizeClass(['v-video-controls', `v-video-controls--variant-${props.variant}`, {
'v-video-controls--pills': props.pills
}, {
'v-video-controls--detached': props.detached
}, {
'v-video-controls--floating': props.floating
}, {
'v-video-controls--fullscreen': props.fullscreen
}, {
'v-video-controls--split-time': props.splitTime
}, !props.pills ? backgroundColorClasses.value : [], props.detached && !props.pills ? elevationClasses.value : [], densityClasses.value, themeClasses.value]),
"style": _normalizeStyle([!props.pills ? backgroundColorStyles.value : [], {
'--v-video-controls-pill-height': `${regularBtnSize}px`
}])
}, [_createVNode(VDefaultsProvider, {
"defaults": innerDefaults
}, {
default: () => [slots.default?.(slotProps) ?? _createElementVNode(_Fragment, null, [props.variant !== 'mini' && _createElementVNode(_Fragment, null, [!props.hidePlay && _createElementVNode("div", {
"class": _normalizeClass([pillClasses, 'v-video__action-play']),
"style": _normalizeStyle(pillStyles)
}, [_withDirectives(_createVNode(VIconBtn, {
"icon": playing.value ? '$pause' : '$play',
"size": playBtnSize,
"aria-label": labels.value.playAction,
"onClick": () => playing.value = !playing.value
}, null), [[vTooltip, labels.value.playAction, 'top']])]), slots.prepend && _createElementVNode("div", {
"class": _normalizeClass(pillClasses),
"style": _normalizeStyle(pillStyles)
}, [slots.prepend(slotProps)]), props.splitTime ? _createElementVNode("span", {
"class": _normalizeClass([pillClasses, 'v-video__time']),
"style": _normalizeStyle(pillStyles)
}, [currentTime.value.elapsed]) : props.variant !== 'default' ? _createElementVNode("span", {
"class": _normalizeClass([pillClasses, 'v-video__time']),
"style": _normalizeStyle(pillStyles)
}, [currentTime.value.elapsed, _createTextVNode(" / "), currentTime.value.total]) : '', props.hideProgressBar ? _createVNode(VSpacer, null, null) : _createVNode(VSlider, {
"modelValue": props.progress,
"noKeyboard": true,
"color": trackColor.value ?? 'surface-variant',
"trackColor": props.variant === 'tube' ? 'white' : undefined,
"class": "v-video__track",
"thumbLabel": "always",
"aria-label": labels.value.seek,
"onUpdate:modelValue": skipTo
}, {
'thumb-label': () => currentTime.value.elapsed
}), props.variant === 'tube' && _createVNode(VSpacer, null, null), props.splitTime ? _createElementVNode("span", {
"class": _normalizeClass([pillClasses, 'v-video__time']),
"style": _normalizeStyle(pillStyles)
}, [currentTime.value.remaining]) : '']), props.variant === 'mini' && _createElementVNode(_Fragment, null, [_createVNode(VSpacer, null, null), slots.prepend && _createElementVNode("div", {
"class": _normalizeClass(pillClasses),
"style": _normalizeStyle(pillStyles)
}, [slots.prepend(slotProps)]), !props.hidePlay && _createElementVNode("div", {
"class": _normalizeClass([pillClasses, 'v-video__action-play']),
"style": _normalizeStyle(pillStyles)
}, [_withDirectives(_createVNode(VIconBtn, {
"icon": playing.value ? '$pause' : '$play',
"size": playBtnSize,
"aria-label": labels.value.playAction,
"onClick": () => playing.value = !playing.value
}, null), [[vTooltip, labels.value.playAction, 'top']])])]), (!props.hideVolume || !props.hideFullscreen || slots.append) && _createElementVNode("div", {
"class": _normalizeClass(pillClasses),
"style": _normalizeStyle(pillStyles)
}, [!props.hideVolume && _createVNode(VVideoVolume, _mergeProps({
"key": "volume-control",
"sliderProps": {
color: props.color
},
"modelValue": volume.value,
"label": labels.value.volumeAction,
"onUpdate:modelValue": v => volume.value = v,
"onClick": () => props.volumeProps?.inline && toggleMuted()
}, props.volumeProps), null), slots.append?.(slotProps), !props.hideFullscreen && _withDirectives(_createVNode(VIconBtn, {
"icon": props.fullscreen ? '$fullscreenExit' : '$fullscreen',
"aria-label": labels.value.fullscreenAction,
"onClick": toggleFullscreen
}, null), [[vTooltip, labels.value.fullscreenAction, 'top']])]), props.variant === 'mini' && _createVNode(VSpacer, null, null)])]
})]);
});
return {
toggleMuted
};
}
});
//# sourceMappingURL=VVideoControls.js.map
File diff suppressed because one or more lines are too long
+151
View File
@@ -0,0 +1,151 @@
@use '../../styles/tools'
@use './variables' as *
@include tools.layer('components')
.v-video-controls
--v-video-controls-height: #{$video-controls-height}
--v-video-controls-pill-border-radius: #{$video-controls-pill-border-radius}
--v-video-controls-gap: #{$video-controls-gap}
--v-background-opacity: #{$video-controls-default-background-opacity}
// override by density logic
--v-video-controls-pill-height: 42px
flex: 1 0 100%
align-self: stretch
padding: $video-controls-padding
display: flex
align-items: center
position: relative
gap: var(--v-video-controls-gap)
min-height: var(--v-video-controls-height)
backdrop-filter: $video-controls-default-backdrop-filter
background-color: rgba($video-controls-default-background-color, var(--v-background-opacity))
color: $video-controls-default-color
width: 100%
opacity: 1
@at-root
@include tools.density('v-video-controls', $video-density) using ($modifier)
--v-video-controls-gap: #{$video-controls-gap + $modifier}
--v-video-controls-height: #{$video-controls-height + $modifier * 2}
&, &--pills > .v-video-control__pill
transition: .6s ease-in-out
transition-property: opacity, background-color
&--pills > .v-video-control__pill
@include tools.elevation($video-elevation)
&:hover
--v-background-opacity: 1
.v-video-control__pill
display: flex
align-items: center
gap: $video-controls-pill-gap
&--pills
--v-background-opacity: 1
--v-video-controls-height: calc(var(--v-video-controls-pill-height) + 24px)
backdrop-filter: none
pointer-events: none
> *
pointer-events: auto
&:not(:empty)
background: transparent
> .v-video-control__pill
backdrop-filter: $video-controls-pill-backdrop-filter
background: rgba($video-controls-default-background-color, var(--v-background-opacity))
border-radius: var(--v-video-controls-pill-border-radius)
min-height: var(--v-video-controls-pill-height)
min-width: var(--v-video-controls-pill-height)
padding: $video-controls-pill-padding
z-index: 1
> .v-icon-btn
border-radius: inherit
&:empty
display: none
&:has(> *:only-child)
padding-inline: 0
justify-content: center
border-radius: var(--v-video-controls-pill-border-radius)
> .v-video__time
padding: $video-time-pill-padding
&--variant-tube
.v-slider.v-video__track
position: absolute
top: $video-tube-track-top
left: 0
right: 0
.v-slider-track
height: calc(var(--v-slider-track-size) + #{$video-tube-track-hover-offset})
.v-slider-track__fill
height: var(--v-slider-track-size)
&--split-time
padding-inline: $video-controls-split-time-padding-inline
&:not(.v-video-controls--floating)
border-bottom-left-radius: inherit
border-bottom-right-radius: inherit
&:not(.v-video-controls--floating):not(.v-video-controls--pills)
border-top: thin solid tools.theme-color('surface-variant', .2)
&--floating
--v-background-opacity: 1
border-radius: inherit
margin-inline: $video-controls-floating-margin
width: calc(100% - 2 * #{$video-controls-floating-margin})
&--detached
--v-background-opacity: 1
position: relative
margin-block-start: $video-controls-detached-offset
padding-inline: $video-controls-detached-padding-inline
backdrop-filter: none
border-top: none
border-radius: inherit
opacity: 1
&:not(.v-video-controls--pills)
@include tools.elevation($video-elevation)
&--fullscreen
position: absolute
bottom: 0
z-index: 2147483647
&.v-video-controls--floating
bottom: $video-controls-floating-margin
.v-video__track
position: relative
z-index: 1
&.v-slider.v-input--horizontal > .v-input__control
min-height: 4px
.v-slider-thumb:not(:hover)
.v-slider-thumb__label
opacity: 0
transition-delay: .2s
.v-slider-thumb__label
color: rgb(var(--v-theme-on-surface-variant)) !important
.v-video__time
font-size: $video-time-font-size
line-height: $video-time-line-height
letter-spacing: $video-time-letter-spacing
+49
View File
@@ -0,0 +1,49 @@
@layer vuetify-components {
.v-video-volume--inline {
display: flex;
align-items: center;
}
.v-video-volume__menu {
background: rgb(var(--v-theme-surface));
color: rgb(var(--v-theme-on-surface));
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
}
.v-video-volume__menu {
border-color: rgba(var(--v-border-color), var(--v-border-opacity));
border-style: solid;
border-width: thin;
}
.v-video-volume__menu--border {
border-width: thin;
box-shadow: none;
}
.v-video-volume__menu {
box-shadow: 0px 1px 2px 0px rgba(var(--v-shadow-color), var(--v-shadow-key-opacity, 0.3)), 0px 1px 3px 1px rgba(var(--v-shadow-color), var(--v-shadow-ambient-opacity, 0.15));
--v-elevation-overlay: color-mix(in srgb, var(--v-elevation-overlay-color) 2%, transparent);
}
.v-video-volume__menu {
border-radius: 4px;
}
.v-video-volume__menu:has(.v-input--horizontal) {
height: 40px;
}
.v-video-volume__menu:has(.v-input--vertical) {
width: 40px;
}
.v-video-volume__menu .v-slider.v-input--horizontal {
margin: 0 16px;
width: 100px;
height: 40px;
}
.v-video-volume__menu .v-slider.v-input--vertical {
margin: 16px 0;
height: 100px;
width: 40px;
}
.v-video-volume__menu .v-slider.v-input--vertical > .v-input__control {
min-height: 50px;
}
}
File diff suppressed because it is too large Load Diff
+103
View File
@@ -0,0 +1,103 @@
import { createVNode as _createVNode, mergeProps as _mergeProps, normalizeClass as _normalizeClass, createElementVNode as _createElementVNode, withDirectives as _withDirectives, normalizeStyle as _normalizeStyle } from "vue";
// Styles
import "./VVideoVolume.css";
// Components
import { VIcon } from "../../components/VIcon/VIcon.js";
import { VMenu } from "../../components/VMenu/VMenu.js";
import { VSlider } from "../../components/VSlider/VSlider.js";
import { VIconBtn } from "../VIconBtn/VIconBtn.js"; // Composables
import { makeComponentProps } from "../../composables/component.js";
import { useLocale } from "../../composables/locale.js";
import { useProxiedModel } from "../../composables/proxiedModel.js"; // Directives
import vTooltip from "../../directives/tooltip/index.js"; // Utilities
import { ref, shallowRef, toRef } from 'vue';
import { EventProp, genericComponent, propsFactory, useRender } from "../../util/index.js"; // Types
export const makeVVideoVolumeProps = propsFactory({
inline: Boolean,
label: String,
direction: {
type: String,
default: 'vertical'
},
modelValue: {
type: Number,
default: 0
},
menuProps: Object,
sliderProps: Object,
onClick: EventProp(),
...makeComponentProps()
}, 'VVideoVolume');
export const VVideoVolume = genericComponent()({
name: 'VVideoVolume',
directives: {
vTooltip: vTooltip
},
props: makeVVideoVolumeProps(),
emits: {
'update:modelValue': val => true
},
setup(props, {
attrs
}) {
const {
t
} = useLocale();
const volume = useProxiedModel(props, 'modelValue');
const volumeIcon = toRef(() => volume.value > 70 ? '$volumeHigh' : volume.value > 40 ? '$volumeMedium' : volume.value > 10 ? '$volumeLow' : '$volumeOff');
const containerRef = ref();
const menu = shallowRef(false);
useRender(() => {
const sliderDefaults = {
hideDetails: true,
step: 5,
thumbSize: 16
};
return _createElementVNode("div", {
"class": _normalizeClass(['v-video-volume', {
'v-video-volume--inline': props.inline
}, props.class]),
"style": _normalizeStyle(props.style),
"ref": containerRef
}, [_withDirectives(_createVNode(VIconBtn, _mergeProps({
"icon": volumeIcon.value,
"aria-label": props.label,
"onClick": props.onClick
}, attrs), {
default: () => [_createVNode(VIcon, null, null), !props.inline && _createVNode(VMenu, {
"modelValue": menu.value,
"onUpdate:modelValue": $event => menu.value = $event,
"activator": "parent",
"attach": containerRef.value,
"closeOnContentClick": false,
"location": props.menuProps?.location ?? 'top center',
"offset": "8"
}, {
default: () => [_createElementVNode("div", {
"class": _normalizeClass(['v-video-volume__menu', `v-video-volume__menu--${props.direction}`])
}, [_createVNode(VSlider, _mergeProps({
"direction": props.direction,
"aria-label": t('$vuetify.video.volume'),
"modelValue": volume.value,
"onUpdate:modelValue": v => volume.value = v
}, sliderDefaults, props.sliderProps), null)])]
})]
}), [[vTooltip, {
text: props.label,
location: 'top',
disabled: menu.value
}]]), props.inline && _createVNode(VSlider, _mergeProps({
"class": "v-video-volume-inline__slider",
"minWidth": "50",
"aria-label": t('$vuetify.video.volume'),
"modelValue": volume.value,
"onUpdate:modelValue": v => volume.value = v,
"onKeydown": e => {
e.stopPropagation();
}
}, sliderDefaults, props.sliderProps), null)]);
});
}
});
//# sourceMappingURL=VVideoVolume.js.map
File diff suppressed because one or more lines are too long
+40
View File
@@ -0,0 +1,40 @@
@use '../../styles/tools'
@use './variables' as *
@include tools.layer('components')
.v-video-volume
&--inline
display: flex
align-items: center
&__menu
background: rgb(var(--v-theme-surface))
color: rgb(var(--v-theme-on-surface))
overflow: hidden
display: flex
align-items: center
justify-content: center
@include tools.border($video-volume-menu-border...)
@include tools.elevation($video-volume-menu-elevation)
@include tools.rounded($video-volume-menu-border-radius)
&:has(.v-input--horizontal)
height: $video-volume-horizontal-menu-height
&:has(.v-input--vertical)
width: $video-volume-vertical-menu-width
.v-slider.v-input--horizontal
margin: $video-volume-horizontal-margin
width: $video-volume-horizontal-menu-width
height: $video-volume-horizontal-menu-height
.v-slider.v-input--vertical
margin: $video-volume-vertical-margin
height: $video-volume-vertical-menu-height
width: $video-volume-vertical-menu-width
> .v-input__control
// just smaller than 300px default
min-height: 50px
+59
View File
@@ -0,0 +1,59 @@
@use 'sass:math';
@use '../../styles/settings';
// Defaults
$video-player-width: 100% !default;
$video-aspect-ratio: math.div(16, 9) !default;
$video-elevation: 2 !default;
$video-padding: 0px !default;
$video-rounded-border-radius: settings.$border-radius-root !default;
$video-controls-floating-margin: 12px !default;
$video-density: ('default': 0, 'comfortable': -1, 'compact': -2) !default;
$video-center-icon-border: 5px solid currentColor !default;
$video-center-icon-opacity: .6 !default;
$video-center-icon-hover-transform: scale(1.05) !default;
$video-tube-track-top: -8px !default;
$video-tube-track-hover-offset: 10px !default;
$video-time-font-size: 0.875rem !default;
$video-time-line-height: 1 !default;
$video-time-letter-spacing: 0.02em !default;
$video-time-pill-padding: 0 16px !default;
$video-controls-height: 58px !default;
$video-controls-padding: 8px !default;
$video-controls-gap: 16px !default;
$video-controls-pill-gap: 8px !default;
$video-controls-pill-padding: 0 6px !default;
$video-controls-pill-border-radius: 99px !default;
$video-controls-pill-backdrop-filter: blur(5px) !default;
$video-controls-default-background-opacity: .8 !default;
$video-controls-default-background-color: var(--v-theme-surface) !default;
$video-controls-default-color: rgb(var(--v-theme-on-surface)) !default;
$video-controls-default-backdrop-filter: blur(5px) !default;
$video-controls-split-time-padding-inline: 20px !default;
$video-controls-detached-offset: 12px !default;
$video-controls-detached-padding-inline: 12px !default;
$video-volume-menu-elevation: 1 !default;
$video-volume-menu-border-radius: settings.$border-radius-root !default;
$video-volume-horizontal-margin: 0 16px !default;
$video-volume-horizontal-menu-width: 100px !default;
$video-volume-horizontal-menu-height: 40px !default;
$video-volume-vertical-margin: 16px 0 !default;
$video-volume-vertical-menu-width: 40px !default;
$video-volume-vertical-menu-height: 100px !default;
$video-volume-menu-border-color: rgba(var(--v-border-color), var(--v-border-opacity)) !default;
$video-volume-menu-border-style: solid !default;
$video-volume-menu-border-width: thin !default;
$video-volume-menu-border-thin-width: thin !default;
$video-volume-menu-border: (
$video-volume-menu-border-color,
$video-volume-menu-border-style,
$video-volume-menu-border-width,
$video-volume-menu-border-thin-width
) !default;
+3
View File
@@ -0,0 +1,3 @@
export { VVideo } from './VVideo.js';
export { VVideoControls } from './VVideoControls.js';
export { VVideoVolume } from './VVideoVolume.js';
+4
View File
@@ -0,0 +1,4 @@
export { VVideo } from "./VVideo.js";
export { VVideoControls } from "./VVideoControls.js";
export { VVideoVolume } from "./VVideoVolume.js";
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","names":["VVideo","VVideoControls","VVideoVolume"],"sources":["../../../src/labs/VVideo/index.ts"],"sourcesContent":["export { VVideo } from './VVideo'\nexport { VVideoControls } from './VVideoControls'\nexport { VVideoVolume } from './VVideoVolume'\n"],"mappings":"SAASA,MAAM;AAAA,SACNC,cAAc;AAAA,SACdC,YAAY","ignoreList":[]}