import '@app/styles.css'; // React Menu sandbox // http://localhost:5173/react-menu/ import { Menu } from '@videojs/react'; import { useState } from 'react'; import { createRoot } from 'react-dom/client'; // ── Class constants ─────────────────────────────────────────────────────────── const menuContentSurfaceClass = 'bg-white border-none ring-1 ring-black/10 shadow-sm rounded-md p-1 min-w-[10rem] overflow-hidden outline-none'; const menuNavSurfaceClass = 'bg-white border-none ring-1 ring-black/10 shadow-sm rounded-md min-w-[10rem] overflow-hidden outline-none'; const menuContentPlacementClass = [ 'data-[side=bottom]:origin-top data-[side=top]:origin-bottom', 'data-[side=left]:origin-right data-[side=right]:origin-left', 'data-starting-style:opacity-0 data-starting-style:scale-95 data-starting-style:-translate-y-1 data-starting-style:blur-sm', 'data-ending-style:opacity-0 data-ending-style:scale-95 data-ending-style:-translate-y-1 data-ending-style:blur-sm', ].join(' '); const menuContentClass = [ menuContentSurfaceClass, 'transition-[opacity,scale,translate,filter] duration-150 ease-in-out', menuContentPlacementClass, ].join(' '); const menuLabelClass = 'block px-2 pt-1.5 pb-0.5 text-xs font-semibold text-slate-500 select-none'; const menuSeparatorClass = 'block h-px bg-slate-200 -mx-1 my-1'; const menuItemClass = [ 'relative flex items-center gap-2 rounded-[calc(0.375rem-2px)] px-2 py-1.5', 'text-sm text-slate-900 cursor-default select-none outline-none transition-colors', 'data-[highlighted]:bg-slate-100', 'aria-disabled:opacity-50 aria-disabled:pointer-events-none', ].join(' '); const radioItemClass = [menuItemClass, 'pl-8'].join(' '); const checkboxItemClass = [menuItemClass, 'pl-8'].join(' '); // Submenu trigger — same as a regular item but with space-between layout const subMenuTriggerClass = [menuItemClass, 'justify-between'].join(' '); const subMenuContentClass = [ 'absolute inset-0 z-10 bg-white rounded-[inherit] p-1 outline-none overflow-hidden translate-x-0', 'transition-transform duration-300 ease-in-out will-change-transform', '[&[data-starting-style][data-direction=forward]]:translate-x-full', '[&[data-ending-style][data-direction=forward]]:-translate-x-full', '[&[data-starting-style][data-direction=back]]:-translate-x-full', '[&[data-ending-style][data-direction=back]]:translate-x-full', ].join(' '); // Root and submenu views share the same viewport so they can slide over each other. const rootViewClass = [ 'absolute inset-0 p-1 translate-x-0', 'transition-transform duration-300 ease-in-out will-change-transform', 'data-[menu-view-state=inactive]:-translate-x-full', ].join(' '); const backButtonClass = [ 'flex items-center gap-1.5 w-full rounded-[calc(0.375rem-2px)] px-2 py-1.5 mb-0.5', 'text-sm font-medium text-slate-500 cursor-default select-none outline-none transition-colors', 'hover:bg-slate-100 hover:text-slate-900', ].join(' '); const menuNavPopupClass = [ 'group relative', menuNavSurfaceClass, 'w-(--media-menu-width) h-(--media-menu-height)', 'transition-[opacity,scale,translate,filter,width,height] duration-150 ease-in-out', menuContentPlacementClass, ].join(' '); // ── Indicators ──────────────────────────────────────────────────────────────── function RadioDot() { return ; } function Checkmark() { return ( ); } // ── Shared trigger button ───────────────────────────────────────────────────── function TriggerButton({ children }: { children: React.ReactNode }) { return (
{'
@videojs/react
Selected: {quality}
Loop: {loop ? 'on' : 'off'}
Quality: {quality} {' · '} Speed: {speed === '1' ? 'Normal' : `${speed}x`}