diff --git a/.github/scripts/bundle-size.js b/.github/scripts/bundle-size.js index 0d2b80ea..a113720d 100644 --- a/.github/scripts/bundle-size.js +++ b/.github/scripts/bundle-size.js @@ -35,7 +35,6 @@ const PACKAGES_DIR = join(ROOT, 'packages'); const SKIP_PACKAGES = new Set([ 'sandbox', - '__tech-preview__', 'react-native', 'skins', 'icons', diff --git a/CLAUDE.md b/CLAUDE.md index 0b9803d6..c8228ecf 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -19,9 +19,6 @@ Refer to **[`CONTRIBUTING.md`](./CONTRIBUTING.md)** for setup, development, and | `packages/react-native` | React Native player integration layer. | | `site/` | Astro‑based docs and website. | -IGNORE `packages/__tech-preview__/` — it's legacy code from the Demuxed demo. Don't reference or -modify it when working in other packages. - ### Dependency Hierarchy ```text diff --git a/biome.json b/biome.json index bc8862f6..842e6a7c 100644 --- a/biome.json +++ b/biome.json @@ -132,8 +132,7 @@ "!**/dist", "!**/examples", "!**/styles/vjs.css", - "!**/packages/*/types", - "!**/packages/__tech-preview__" + "!**/packages/*/types" ] } } diff --git a/packages/__tech-preview__/core/CHANGELOG.md b/packages/__tech-preview__/core/CHANGELOG.md deleted file mode 100644 index 6019d27a..00000000 --- a/packages/__tech-preview__/core/CHANGELOG.md +++ /dev/null @@ -1,190 +0,0 @@ -# Changelog - -## [0.1.0-preview.10](https://github.com/videojs/v10/compare/@videojs/core@0.1.0-preview.9...@videojs/core@0.1.0-preview.10) (2025-12-06) - - -### Features - -* add tooltip core ([#212](https://github.com/videojs/v10/issues/212)) ([cbf41ce](https://github.com/videojs/v10/commit/cbf41ce4c750cd5c3bb6cbf247bad91ccd578cd0)) - - -### Bug Fixes - -* add popover core, use in html and improve factory ([#204](https://github.com/videojs/v10/issues/204)) ([f3b1b19](https://github.com/videojs/v10/commit/f3b1b199173f3750bc05ad9063fcccbd4163b12b)) -* use popover core in react popover ([#208](https://github.com/videojs/v10/issues/208)) ([99fef78](https://github.com/videojs/v10/commit/99fef78f63e8dd121513b9cd20696a7d35603837)) - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/utils bumped to 0.1.0-preview.10 - -## [0.1.0-preview.9](https://github.com/videojs/v10/compare/@videojs/core@0.1.0-preview.8...@videojs/core@0.1.0-preview.9) (2025-11-18) - - -### Bug Fixes - -* clean up core, less seams in wrappers ([#197](https://github.com/videojs/v10/issues/197)) ([601a5a3](https://github.com/videojs/v10/commit/601a5a37939605ca38f81a825fd02934e7928c39)) - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/utils bumped to 0.1.0-preview.9 - -## [0.1.0-preview.8](https://github.com/videojs/v10/compare/@videojs/core@0.1.0-preview.7...@videojs/core@0.1.0-preview.8) (2025-11-12) - - -### Miscellaneous Chores - -* **@videojs/core:** Synchronize videojs versions - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/utils bumped to 0.1.0-preview.8 - -## [0.1.0-preview.7](https://github.com/videojs/v10/compare/@videojs/core@0.1.0-preview.6...@videojs/core@0.1.0-preview.7) (2025-11-11) - - -### Miscellaneous Chores - -* **@videojs/core:** Synchronize videojs versions - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/utils bumped to 0.1.0-preview.7 - -## [0.1.0-preview.6](https://github.com/videojs/v10/compare/@videojs/core@0.1.0-preview.5...@videojs/core@0.1.0-preview.6) (2025-11-06) - - -### Bug Fixes - -* **site:** update discord link ([#170](https://github.com/videojs/v10/issues/170)) ([bb10294](https://github.com/videojs/v10/commit/bb10294419439fb02df650f3e7f7c5496ecc3a73)) - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/utils bumped to 0.1.0-preview.6 - -## [0.1.0-preview.5](https://github.com/videojs/v10/compare/@videojs/core@0.1.0-preview.4...@videojs/core@0.1.0-preview.5) (2025-11-03) - - -### Miscellaneous Chores - -* **@videojs/core:** Synchronize videojs versions - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/utils bumped to 0.1.0-preview.5 - -## [0.1.0-preview.4](https://github.com/videojs/v10/compare/@videojs/core@0.1.0-preview.3...@videojs/core@0.1.0-preview.4) (2025-10-30) - - -### Miscellaneous Chores - -* **@videojs/core:** Synchronize videojs versions - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/utils bumped to 0.1.0-preview.4 - -## [0.1.0-preview.3](https://github.com/videojs/v10/compare/@videojs/core@0.1.0-preview.2...@videojs/core@0.1.0-preview.3) (2025-10-29) - - -### Bug Fixes - -* **packages:** update version badges ([#138](https://github.com/videojs/v10/issues/138)) ([22d9cb6](https://github.com/videojs/v10/commit/22d9cb64f2e5b9601a2039bb166dbe3fee6a1b3e)) - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/utils bumped to 0.1.0-preview.3 - -## [0.1.0-preview.2](https://github.com/videojs/v10/compare/@videojs/core@0.1.0-preview.1...@videojs/core@0.1.0-preview.2) (2025-10-25) - - -### Bug Fixes - -* **core:** update README to use v10 terminology ([93dd266](https://github.com/videojs/v10/commit/93dd266f551923e103111019e12834f27cefabe7)) - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/utils bumped to 0.1.0-preview.2 - -## [0.1.0-preview.1](https://github.com/videojs/v10/compare/@videojs/core@0.1.0-preview.0...@videojs/core@0.1.0-preview.1) (2025-10-25) - - -### Features - -* add a solution for React preview time display ([#50](https://github.com/videojs/v10/issues/50)) ([f78b09f](https://github.com/videojs/v10/commit/f78b09fd16b7a9ee5a404c9260e3e764fb77ddde)) -* add compound html timerange component ([#14](https://github.com/videojs/v10/issues/14)) ([017ecdb](https://github.com/videojs/v10/commit/017ecdbff991d140ea42e4a855269a54e0a19adc)) -* add core range, time and volume range ([#23](https://github.com/videojs/v10/issues/23)) ([687b765](https://github.com/videojs/v10/commit/687b7655b0b6356c28663ca85c8f6d25a1023c18)) -* add display click to play / pause ([#117](https://github.com/videojs/v10/issues/117)) ([4f06ef6](https://github.com/videojs/v10/commit/4f06ef6c7684fd7064ca76685003a1c38ebd09cd)) -* add keyboard control to sliders ([#115](https://github.com/videojs/v10/issues/115)) ([0a49026](https://github.com/videojs/v10/commit/0a4902623d58f51055b1cc65498a0e716533ec29)) -* add range orientation to react components ([#30](https://github.com/videojs/v10/issues/30)) ([5e3cb8a](https://github.com/videojs/v10/commit/5e3cb8ad8134ecaf20b6e021e301a38b8ed06de2)) -* **core,html,react:** implement VolumeRange component with integrated state management ([2282f47](https://github.com/videojs/v10/commit/2282f4799b1c3fc3c55473bdfc2def86384d5d19)) -* **core:** implement temporal state management for time-based media controls ([1e12f66](https://github.com/videojs/v10/commit/1e12f66f16eedfe13af17ccfabca7ab0e8239313)) -* **icons:** add fullscreen enter and exit icons ([29ed5e3](https://github.com/videojs/v10/commit/29ed5e37593d7b5b40dba36bbadac5eed71c2710)) -* **icons:** implement shared SVG icon system across packages ([e0be58e](https://github.com/videojs/v10/commit/e0be58e094e65ea72af99b2c1e1d87c507c251bd)) -* implement current time display components ([5bd0a15](https://github.com/videojs/v10/commit/5bd0a154dbba01d2a5d11eb1f548fe4baa581675)) -* initialize Video.js 10 monorepo with core architecture ([4b0d84e](https://github.com/videojs/v10/commit/4b0d84e9c8adfa7401084389da5deb751420b629)) -* **media-store,html,react:** implement TimeRange component with hook-style architecture ([c29fd2c](https://github.com/videojs/v10/commit/c29fd2c2c1edd61c09a6683041c709a990d8a6f0)) -* **media-store:** add comprehensive time formatting utilities ([7e26e54](https://github.com/videojs/v10/commit/7e26e547ea0b98c6366d6cf1b3800b5f253767a7)) -* **media-store:** add duration display component state definition ([d0036fc](https://github.com/videojs/v10/commit/d0036fc7d68b4e50c5c7af863105900f20bee12c)) -* **media-store:** add fullscreen button component state definition ([e19b98b](https://github.com/videojs/v10/commit/e19b98b1200f80f5303b4c8a76948254c95a8c81)) -* **media-store:** add fullscreen state mediator with shadow DOM support ([7f243c2](https://github.com/videojs/v10/commit/7f243c22d85f45c5f7a88e3638f03ce774667d20)) -* migrate entire monorepo from tsc to tsup for production builds ([7403cc7](https://github.com/videojs/v10/commit/7403cc728119322888e527468a07a7634f43b32a)) -* **monorepo:** migrate prototype code to organized package structure ([4b472ec](https://github.com/videojs/v10/commit/4b472ec49cd91f0af61cb5aaa039d428982d3b91)) -* **react-icons:** implement SVGR-powered auto-generation with full styling support ([6fcb18f](https://github.com/videojs/v10/commit/6fcb18f1d2b108990025f8ea67b3a31e17879d49)) -* rename range to slider ([#46](https://github.com/videojs/v10/issues/46)) ([9c6eaef](https://github.com/videojs/v10/commit/9c6eaef2aa61771ae1407d0a594b3f790e0ff665)) -* **ui:** micro icons, toasted design tweaks ([#52](https://github.com/videojs/v10/issues/52)) ([bd3f0f7](https://github.com/videojs/v10/commit/bd3f0f7510480125653506d8e2e560234f6c06f2)) -* **ui:** port over default skin ([ff4ea36](https://github.com/videojs/v10/commit/ff4ea3693e63ab3b5a728988ca44f3bab669e8ff)) -* **ui:** port over default skin ([9950945](https://github.com/videojs/v10/commit/995094500823e1063e7ae291c9a2ea9a4aa74847)) -* **ui:** skin and icon tweaks ([#59](https://github.com/videojs/v10/issues/59)) ([cdebece](https://github.com/videojs/v10/commit/cdebece1ebe8e5160b90e49ac2cbf05a7e0b6dca)) - - -### Bug Fixes - -* Clean up more typescript errors. ([87105db](https://github.com/videojs/v10/commit/87105db6be31038fc92862c240898984d02932eb)) -* design tweaks to toasted skin, lint rule tweaks ([#44](https://github.com/videojs/v10/issues/44)) ([3a0767c](https://github.com/videojs/v10/commit/3a0767c3407b2d6d8af3d3a8afd57b1e76efda85)) -* enable eslint & run eslint:fix ([#43](https://github.com/videojs/v10/issues/43)) ([5cb93a1](https://github.com/videojs/v10/commit/5cb93a14a7f47d66d5c71f9b82867621beda236c)) -* **icons:** add currentColor fill to fullscreen icons for proper theming ([d0d4876](https://github.com/videojs/v10/commit/d0d487601eb2da669be9a83cc8201d94998ec334)) -* **media-store:** replace tsup with rollup for consistent build tooling ([77c2932](https://github.com/videojs/v10/commit/77c2932b8d504a3c66e0af3badf3b1332d5d92c0)) -* **media-store:** resolve TypeScript declaration generation build issues ([d8809c5](https://github.com/videojs/v10/commit/d8809c5abe066a39a14b423be239fe7c0c234632)) -* **media-store:** resolve TypeScript error in dispatch method ([3fd26eb](https://github.com/videojs/v10/commit/3fd26eb6544809d6a3e00a45e916f5e9fd61ae2a)) -* **media:** use explicit exports to resolve React package TypeScript errors ([4662dbc](https://github.com/videojs/v10/commit/4662dbcbb1811293c98c803f8057a2534114d4ea)) -* minimal volume slider bug & fix dev infinite bug ([#73](https://github.com/videojs/v10/issues/73)) ([591dab6](https://github.com/videojs/v10/commit/591dab66caf8829017688007320f92b7445c4baa)) -* resolve package dependency and TypeScript export issues ([7154b1e](https://github.com/videojs/v10/commit/7154b1e44674a61735ab0f393a8bed3fcc689f8d)) -* resolve TypeScript build errors across packages ([374db7a](https://github.com/videojs/v10/commit/374db7afc07d6211bfd3f8079bbcd9613f3b69f3)) -* **root:** add videojs keyword to package.json ([4a9f8b2](https://github.com/videojs/v10/commit/4a9f8b2ad6fb27b463dcfe8d1a5fd883c9fa21d1)) -* seek jump back to current time ([#22](https://github.com/videojs/v10/issues/22)) ([a3f9630](https://github.com/videojs/v10/commit/a3f9630bd1eb34a16f339ffd30071b8adc864ca0)) -* **time-display:** clean up time utilities and simplify components ([597e79d](https://github.com/videojs/v10/commit/597e79d7fc12737353c8c9eb3f6e77ef0a04e9ed)) -* **typescript:** resolve declaration file generation for rollup packages ([#1](https://github.com/videojs/v10/issues/1)) ([69670e8](https://github.com/videojs/v10/commit/69670e8d7134db34aee665d8871cd17901625915)) - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/utils bumped to 0.1.0-preview.1 diff --git a/packages/__tech-preview__/core/README.md b/packages/__tech-preview__/core/README.md deleted file mode 100644 index 259381eb..00000000 --- a/packages/__tech-preview__/core/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# @videojs/core - -[![package-badge]][package] - -> **⚠️ Technical Preview - SUBJECT TO CHANGE** Not recommended for production use. - -## Overview - -`@videojs/core` provides runtime-agnostic core components and utilities shared across Video.js -packages. These are foundational building blocks used by platform-specific implementations -(HTML, React, React Native) to create consistent media player experiences. - -## Community - -If you need help with anything related to Video.js v10, or if you'd like to casually chat with other -members: - -- [Join Discord Server][discord] -- [See GitHub Discussions][gh-discussions] - -## License - -[Apache-2.0](./LICENSE) - -[package]: https://www.npmjs.com/package/@videojs/core -[package-badge]: https://img.shields.io/npm/v/@videojs/core/next?label=@videojs/core@next -[discord]: https://discord.gg/JBqHh485uF -[gh-discussions]: https://github.com/videojs/v10/discussions diff --git a/packages/__tech-preview__/core/package.json b/packages/__tech-preview__/core/package.json deleted file mode 100644 index 3d15e2ff..00000000 --- a/packages/__tech-preview__/core/package.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "name": "@videojs/core-preview", - "type": "module", - "version": "0.1.0-preview.10", - "description": "Core components and utilities for Video.js", - "license": "Apache-2.0", - "keywords": [ - "core", - "components", - "media", - "player", - "ui", - "time-range", - "videojs" - ], - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "./media": { - "types": "./dist/media.d.ts", - "default": "./dist/media.js" - }, - "./store": { - "types": "./dist/store.d.ts", - "default": "./dist/store.js" - } - }, - "main": "dist/index.js", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist" - ], - "scripts": { - "build": "tsdown", - "build:watch": "tsdown --watch --silent", - "dev": "pnpm run build:watch", - "test": "echo \"No tests yet\"", - "clean": "rm -rf dist" - }, - "dependencies": { - "@videojs/utils-preview": "workspace:*", - "hls.js": "^1.6.7", - "nanostores": "^1.0.1" - }, - "devDependencies": { - "tsdown": "^0.15.9", - "typescript": "^5.9.3" - }, - "publishConfig": { - "access": "public" - } -} diff --git a/packages/__tech-preview__/core/src/components/popover.ts b/packages/__tech-preview__/core/src/components/popover.ts deleted file mode 100644 index d5ecd959..00000000 --- a/packages/__tech-preview__/core/src/components/popover.ts +++ /dev/null @@ -1,319 +0,0 @@ -import { shallowEqual } from '@videojs/utils-preview'; -import { - addTranslateToBoundingRect, - contains, - getDocument, - getInBoundsAdjustments, - getUntransformedBoundingRect, - safePolygon, -} from '@videojs/utils-preview/dom'; -import { map } from 'nanostores'; - -type Placement = 'top' | 'top-start' | 'top-end'; - -export interface PopoverState { - openOnHover: boolean; - delay: number; - closeDelay: number; - placement: Placement; - sideOffset: number; - collisionPadding: number; - disableHoverablePopover: boolean; - trackCursorAxis?: 'x'; - _open: boolean; - _transitionStatus: 'initial' | 'open' | 'close' | 'unmounted'; - _collisionOffset: { x: number }; - _pointerPosition: { x: number }; - _setTriggerElement: (element: HTMLElement | null) => void; - _triggerElement?: HTMLElement | null; - _setPopoverElement: (element: HTMLElement | null) => void; - _popoverElement?: HTMLElement | null; - _setCollisionBoundaryElement: (element: HTMLElement | null) => void; - _collisionBoundaryElement?: HTMLElement | null; - _popoverStyle?: Partial; - _minLeft?: number; - _maxLeft?: number; -} - -export class Popover { - #hoverTimeout: ReturnType | null = null; - #resizeObserver: ResizeObserver | null = null; - #state = map({ - openOnHover: false, - delay: 0, - closeDelay: 0, - placement: 'top', - sideOffset: 5, - collisionPadding: 0, - disableHoverablePopover: false, - _open: false, - _transitionStatus: 'initial', - _collisionOffset: { x: 0 }, - _pointerPosition: { x: 0 }, - _setTriggerElement: this._setTriggerElement.bind(this), - _setPopoverElement: this._setPopoverElement.bind(this), - _setCollisionBoundaryElement: this._setCollisionBoundaryElement.bind(this), - }); - - _setPopoverElement(element: HTMLElement | null): void { - if (!element) { - this.#resizeObserver?.disconnect(); - this.#clearHoverTimeout(); - this.#popoverElement?.removeEventListener('pointerenter', this); - this.#popoverElement?.removeEventListener('focusout', this); - getDocument(this.#popoverElement).documentElement.removeEventListener('pointermove', this); - return; - } - - this.setState({ _popoverElement: element }); - - this.#resizeObserver = new ResizeObserver(() => this.#checkCollision()); - this.#resizeObserver.observe(element); - - element.addEventListener('pointerenter', this); - element.addEventListener('focusout', this); - } - - _setTriggerElement(element: HTMLElement | null): void { - if (!element) { - this.#triggerElement?.removeEventListener('pointerenter', this); - this.#triggerElement?.removeEventListener('pointerleave', this); - this.#triggerElement?.removeEventListener('focusin', this); - this.#triggerElement?.removeEventListener('focusout', this); - return; - } - - this.setState({ _triggerElement: element }); - - if (globalThis.matchMedia?.('(hover: hover)')?.matches) { - element.addEventListener('pointerenter', this); - element.addEventListener('pointerleave', this); - } - - element.addEventListener('focusin', this); - element.addEventListener('focusout', this); - } - - _setCollisionBoundaryElement(element: HTMLElement | null): void { - this.setState({ _collisionBoundaryElement: element }); - } - - subscribe(callback: (state: PopoverState) => void): () => void { - return this.#state.subscribe(callback); - } - - setState(state: Partial): void { - if (shallowEqual(state, this.#state.get())) return; - this.#state.set({ ...this.#state.get(), ...state }); - } - - getState(): PopoverState { - const baseState = this.#state.get(); - const { - placement, - sideOffset, - trackCursorAxis, - _popoverElement, - _pointerPosition, - _collisionOffset, - _minLeft, - _maxLeft, - } = baseState; - const [side, alignment] = placement.split('-'); - - const _popoverStyle: Partial = { - ...(_popoverElement ? { positionAnchor: `--${_popoverElement.id}` } : {}), - top: `calc(anchor(${side}) - ${sideOffset}px)`, - }; - - if (trackCursorAxis === 'x') { - _popoverStyle.translate = `-50% -100%`; - _popoverStyle.left = `clamp(${_minLeft}px, ${_pointerPosition.x}px, ${_maxLeft}px)`; - } else { - _popoverStyle.translate = `${_collisionOffset.x}px -100%`; - _popoverStyle.justifySelf - = alignment === 'start' ? 'anchor-start' : alignment === 'end' ? 'anchor-end' : 'anchor-center'; - } - - return { - ...baseState, - _popoverStyle, - }; - } - - handleEvent(event: Event): void { - switch (event.type) { - case 'pointerenter': - this.#handlePointerEnter(event as PointerEvent); - break; - case 'pointerleave': - this.#handlePointerLeave(event as PointerEvent); - break; - case 'pointermove': - this.#handlePointerMove(event as PointerEvent); - break; - case 'focusin': - this.#handleFocusIn(event as FocusEvent); - break; - case 'focusout': - this.#handleFocusOut(event as FocusEvent); - break; - default: - break; - } - } - - get #popoverElement(): HTMLElement | undefined | null { - return this.getState()._popoverElement; - } - - get #triggerElement(): HTMLElement | undefined | null { - return this.getState()._triggerElement; - } - - get #open(): boolean { - return this.getState()._open; - } - - #setOpen(open: boolean): void { - if (this.#open === open) return; - - this.setState({ _open: open }); - - if (open) { - this.setState({ _transitionStatus: 'initial' }); - - this.#popoverElement?.showPopover(); - - requestAnimationFrame(() => { - this.setState({ _transitionStatus: 'open' }); - - // This requestAnimationFrame is required for React because it renders async. - requestAnimationFrame(() => this.#checkCollision()); - }); - } else { - this.setState({ _transitionStatus: 'close' }); - - // This requestAnimationFrame is required for React because the data- attributes are not updated immediately. - requestAnimationFrame(() => { - const transitions = this.#popoverElement?.getAnimations().filter(anim => anim instanceof CSSTransition); - if (transitions && transitions.length > 0) { - Promise.all(transitions.map(t => t.finished)) - .then(() => this.#popoverElement?.hidePopover()) - .catch(() => this.#popoverElement?.hidePopover()); - } else { - this.#popoverElement?.hidePopover(); - } - }); - } - } - - #clearHoverTimeout(): void { - if (this.#hoverTimeout) { - globalThis.clearTimeout(this.#hoverTimeout); - this.#hoverTimeout = null; - } - } - - #handlePointerEnter(event: PointerEvent): void { - const { openOnHover, trackCursorAxis } = this.getState(); - if (!openOnHover) return; - - this.#clearHoverTimeout(); - - if (event.currentTarget === this.#popoverElement || trackCursorAxis === 'x') { - this.#addPointerMoveListener(); - } - - if (this.getState()._open) { - return; - } - - this.#hoverTimeout = globalThis.setTimeout(() => { - this.#setOpen(true); - }, this.getState().delay); - } - - #handlePointerLeave(_event: PointerEvent): void { - const { disableHoverablePopover, closeDelay } = this.getState(); - - if (!disableHoverablePopover) { - this.#addPointerMoveListener(); - } - - if (disableHoverablePopover) { - this.#clearHoverTimeout(); - this.#hoverTimeout = globalThis.setTimeout(() => { - this.#setOpen(false); - }, closeDelay); - } - } - - #addPointerMoveListener(): void { - if (!globalThis.matchMedia?.('(hover: hover)')?.matches || !this.#popoverElement) return; - - getDocument(this.#popoverElement).documentElement.addEventListener('pointermove', this); - } - - #handlePointerMove(event: PointerEvent): void { - const { disableHoverablePopover, openOnHover, trackCursorAxis } = this.getState(); - if (!openOnHover || !this.#triggerElement || !this.#popoverElement) return; - - if (trackCursorAxis === 'x') { - this.setState({ _pointerPosition: { x: event.clientX } }); - - // This requestAnimationFrame is required for React because it renders async. - requestAnimationFrame(() => this.#checkCollision()); - } - - if (disableHoverablePopover) { - return; - } - - const close = safePolygon({ blockPointerEvents: true })({ - placement: this.getState().placement, - elements: { - domReference: this.#triggerElement, - floating: this.#popoverElement, - }, - x: event.clientX, - y: event.clientY, - onClose: () => { - getDocument(this.#popoverElement).documentElement.removeEventListener('pointermove', this); - - this.#clearHoverTimeout(); - this.#hoverTimeout = globalThis.setTimeout(() => { - this.#setOpen(false); - }, this.getState().closeDelay); - }, - }); - close(event); - } - - #handleFocusIn(_event: FocusEvent): void { - this.#setOpen(true); - } - - #handleFocusOut(event: FocusEvent): void { - const relatedTarget = event.relatedTarget as HTMLElement; - if (relatedTarget && contains(this.#popoverElement, relatedTarget)) return; - - this.#setOpen(false); - } - - #checkCollision(): void { - const { _collisionBoundaryElement, _open, _popoverElement, collisionPadding } = this.getState(); - - if (!_open || !_collisionBoundaryElement || !_popoverElement) return; - - const popoverRect = getUntransformedBoundingRect(_popoverElement); - const boundsRect = getUntransformedBoundingRect(_collisionBoundaryElement); - const translatedPopoverRect = addTranslateToBoundingRect(popoverRect, _popoverElement); - - const _minLeft = boundsRect.left + translatedPopoverRect.width / 2 + collisionPadding; - const _maxLeft = boundsRect.right - translatedPopoverRect.width / 2 - collisionPadding; - const _collisionOffset = getInBoundsAdjustments(popoverRect, boundsRect, collisionPadding); - - this.setState({ _minLeft, _maxLeft, _collisionOffset }); - } -} diff --git a/packages/__tech-preview__/core/src/components/slider.ts b/packages/__tech-preview__/core/src/components/slider.ts deleted file mode 100644 index 63f5d822..00000000 --- a/packages/__tech-preview__/core/src/components/slider.ts +++ /dev/null @@ -1,176 +0,0 @@ -import { getPointProgressOnLine, shallowEqual } from '@videojs/utils-preview'; -import { map } from 'nanostores'; - -export interface SliderState { - _setRootElement: (element: HTMLElement | null) => void; - _rootElement: HTMLElement | null; - _setTrackElement: (element: HTMLElement | null) => void; - _trackElement: HTMLElement | null; - _pointerRatio: number; - _hovering: boolean; - _dragging: boolean; - _keying: boolean; - _fillWidth: number; - _pointerWidth: number; - _stepSize: number; -} - -export class Slider { - #abortController: AbortController | null = null; - #state = map({ - _setRootElement: this._setRootElement.bind(this), - _rootElement: null, - _setTrackElement: (element: HTMLElement | null) => this.setState({ _trackElement: element }), - _trackElement: null, - _pointerRatio: 0, - _hovering: false, - _dragging: false, - _keying: false, - _fillWidth: 0, - _pointerWidth: 0, - _stepSize: 0.01, - }); - - _setRootElement(element: HTMLElement | null): void { - this.setState({ _rootElement: element }); - - if (!element) { - this.#abortController?.abort(); - this.#abortController = null; - return; - } - - this.#abortController = new AbortController(); - const { signal } = this.#abortController; - - element.addEventListener('pointerdown', this, { signal }); - element.addEventListener('pointermove', this, { signal }); - element.addEventListener('pointerup', this, { signal }); - element.addEventListener('pointerenter', this, { signal }); - element.addEventListener('pointerleave', this, { signal }); - element.addEventListener('keydown', this, { signal }); - element.addEventListener('keyup', this, { signal }); - } - - subscribe(callback: (state: SliderState) => void): () => void { - return this.#state.subscribe(callback); - } - - setState(state: Partial): void { - if (shallowEqual(state, this.#state.get())) return; - this.#state.set({ ...this.#state.get(), ...state }); - } - - getState(): SliderState { - const state = this.#state.get(); - - let _pointerWidth = 0; - if (state._hovering) { - _pointerWidth = state._pointerRatio; - } - - return { ...state, _pointerWidth }; - } - - handleEvent(event: Event): void { - const { type } = event; - switch (type) { - case 'pointerdown': - this.#handlePointerDown(event as PointerEvent); - break; - case 'pointermove': - this.#handlePointerMove(event as PointerEvent); - break; - case 'pointerup': - this.#handlePointerUp(event as PointerEvent); - break; - case 'pointerenter': - this.#handlePointerEnter(event as PointerEvent); - break; - case 'pointerleave': - this.#handlePointerLeave(event as PointerEvent); - break; - case 'keydown': - this.#handleKeyDown(event as KeyboardEvent); - break; - case 'keyup': - this.#handleKeyUp(event as KeyboardEvent); - break; - } - } - - getPointerRatio(evt: PointerEvent): number { - const { _trackElement } = this.#state.get(); - if (!_trackElement) return 0; - - const rect = _trackElement.getBoundingClientRect(); - return getPointProgressOnLine( - evt.clientX, - evt.clientY, - { x: rect.left, y: rect.bottom }, - { x: rect.right, y: rect.top }, - ); - } - - #handlePointerDown(event: PointerEvent) { - this.#state.get()._rootElement?.setPointerCapture(event.pointerId); - this.setState({ _pointerRatio: this.getPointerRatio(event), _dragging: true }); - } - - #handlePointerMove(event: PointerEvent) { - this.setState({ _pointerRatio: this.getPointerRatio(event) }); - } - - #handlePointerUp(event: PointerEvent) { - this.setState({ _pointerRatio: this.getPointerRatio(event), _dragging: false }); - this.#state.get()._rootElement?.releasePointerCapture(event.pointerId); - } - - #handlePointerEnter(_event: PointerEvent) { - this.setState({ _hovering: true }); - } - - #handlePointerLeave(_event: PointerEvent) { - this.setState({ _hovering: false }); - } - - #handleKeyDown(event: KeyboardEvent) { - const { key } = event; - const { _pointerRatio, _stepSize } = this.#state.get(); - - let newRatio = _pointerRatio; - - switch (key) { - case 'ArrowLeft': - case 'ArrowDown': - event.preventDefault(); - newRatio = Math.max(0, _pointerRatio - _stepSize); - break; - case 'ArrowRight': - case 'ArrowUp': - event.preventDefault(); - newRatio = Math.min(1, _pointerRatio + _stepSize); - break; - case 'Home': - event.preventDefault(); - newRatio = 0; - break; - case 'End': - event.preventDefault(); - newRatio = 1; - break; - default: - return; // Don't update state for other keys - } - - this.setState({ _pointerRatio: newRatio, _keying: true }); - } - - #handleKeyUp(_event: KeyboardEvent) { - this.setState({ _keying: false }); - } - - setStepSize(stepSize: number): void { - this.setState({ _stepSize: Math.max(0.001, Math.min(1, stepSize)) }); - } -} diff --git a/packages/__tech-preview__/core/src/components/time-slider.ts b/packages/__tech-preview__/core/src/components/time-slider.ts deleted file mode 100644 index 05e3e44c..00000000 --- a/packages/__tech-preview__/core/src/components/time-slider.ts +++ /dev/null @@ -1,118 +0,0 @@ -import type { SliderState } from './slider'; - -import { formatTime } from '@videojs/utils-preview'; -import { Slider } from './slider'; - -export interface TimeSliderState extends SliderState { - currentTime: number; - duration: number; - requestSeek: (time: number) => void; - requestPreview: (time: number) => void; - _currentTimeText: string; - _durationText: string; -} - -export class TimeSlider extends Slider { - #seekingTime: number | null = null; - #oldCurrentTime: number | null = null; - - getState(): TimeSliderState { - const state = super.getState() as TimeSliderState; - - // When dragging or keying, use pointer position for immediate feedback; - // While seeking, use seeking time so it doesn't jump back to the current time; - // Otherwise, use current time; - let _fillWidth = 0; - if (state._dragging || state._keying) { - _fillWidth = state._pointerRatio * 100; - } else if (state.duration > 0) { - if (this.#seekingTime !== null && this.#oldCurrentTime === state.currentTime) { - _fillWidth = (this.#seekingTime / state.duration) * 100; - } else { - _fillWidth = (state.currentTime / state.duration) * 100; - this.#seekingTime = null; - } - } - - this.#oldCurrentTime = state.currentTime; - - const _currentTimeText = formatTime(state.currentTime); - const _durationText = formatTime(state.duration); - - return { ...state, _fillWidth, _currentTimeText, _durationText }; - } - - setState(newState: Partial): void { - const state = this.getState(); - // When not dragging or keying, set pointer ratio to current time / duration. - if (!state._hovering && !state._dragging && !state._keying && newState.currentTime && newState.duration) { - super.setState({ ...newState, _pointerRatio: newState.currentTime / newState.duration }); - return; - } - super.setState(newState); - } - - handleEvent(event: Event): void { - const { type } = event; - switch (type) { - case 'pointerdown': - this.#handlePointerDown(event as PointerEvent); - break; - case 'pointermove': - this.#handlePointerMove(event as PointerEvent); - break; - case 'pointerup': - this.#handlePointerUp(event as PointerEvent); - break; - case 'keydown': - this.#handleKeyDown(event as KeyboardEvent); - break; - default: - super.handleEvent(event); - break; - } - } - - #handlePointerDown(event: PointerEvent) { - super.handleEvent(event); - - const { _pointerRatio, duration, requestSeek } = super.getState() as TimeSliderState; - - this.#seekingTime = _pointerRatio * duration; - requestSeek(this.#seekingTime); - } - - #handlePointerMove(event: PointerEvent) { - super.handleEvent(event); - - const { _dragging, _pointerRatio, duration, requestSeek, requestPreview } = super.getState() as TimeSliderState; - - const previewTime = _pointerRatio * duration; - requestPreview(previewTime); - - if (_dragging) { - this.#seekingTime = previewTime; - requestSeek(this.#seekingTime); - } - } - - #handlePointerUp(event: PointerEvent) { - const { _dragging, _pointerRatio, duration, requestSeek } = super.getState() as TimeSliderState; - - if (_dragging) { - this.#seekingTime = _pointerRatio * duration; - requestSeek(this.#seekingTime); - } - - super.handleEvent(event); - } - - #handleKeyDown(event: KeyboardEvent) { - super.handleEvent(event); - - const { _pointerRatio, duration, requestSeek } = super.getState() as TimeSliderState; - - this.#seekingTime = _pointerRatio * duration; - requestSeek(this.#seekingTime); - } -} diff --git a/packages/__tech-preview__/core/src/components/tooltip.ts b/packages/__tech-preview__/core/src/components/tooltip.ts deleted file mode 100644 index 9135c744..00000000 --- a/packages/__tech-preview__/core/src/components/tooltip.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { PopoverState } from './popover'; -import { Popover } from './popover'; - -export interface TooltipState extends PopoverState {} - -export class Tooltip extends Popover { - constructor() { - super(); - this.setState({ - openOnHover: true, - disableHoverablePopover: true, - }); - } -} diff --git a/packages/__tech-preview__/core/src/components/volume-slider.ts b/packages/__tech-preview__/core/src/components/volume-slider.ts deleted file mode 100644 index e62307f7..00000000 --- a/packages/__tech-preview__/core/src/components/volume-slider.ts +++ /dev/null @@ -1,104 +0,0 @@ -import type { SliderState } from './slider'; - -import { Slider } from './slider'; - -export interface VolumeSliderState extends SliderState { - volume: number; - muted: boolean; - volumeLevel: string; - requestVolumeChange: (volume: number) => void; - _volumeText: string; -} - -export class VolumeSlider extends Slider { - constructor() { - super(); - this.setStepSize(0.1); - } - - getState(): VolumeSliderState { - const state = super.getState() as VolumeSliderState; - - // When dragging or keying, use pointer position for immediate feedback; - // Otherwise, use current volume; - let _fillWidth = 0; - if (state._dragging || state._keying) { - _fillWidth = state._pointerRatio * 100; - } else { - _fillWidth = state.muted ? 0 : (state.volume || 0) * 100; - } - - const _volumeText = formatVolume(state.muted ? 0 : state.volume || 0); - - return { ...state, _fillWidth, _volumeText }; - } - - setState(newState: Partial): void { - const state = this.getState(); - // When not dragging or keying, set pointer ratio to current volume. - if (!state._dragging && !state._keying && newState.volume) { - super.setState({ ...newState, _pointerRatio: newState.volume }); - return; - } - super.setState(newState); - } - - handleEvent(event: Event): void { - const { type } = event; - switch (type) { - case 'pointerdown': - this.#handlePointerDown(event as PointerEvent); - break; - case 'pointermove': - this.#handlePointerMove(event as PointerEvent); - break; - case 'pointerup': - this.#handlePointerUp(event as PointerEvent); - break; - case 'keydown': - this.#handleKeyDown(event as KeyboardEvent); - break; - default: - super.handleEvent(event); - break; - } - } - - #handlePointerDown(event: PointerEvent) { - super.handleEvent(event); - - const { _pointerRatio, requestVolumeChange } = super.getState() as VolumeSliderState; - requestVolumeChange(_pointerRatio); - } - - #handlePointerMove(event: PointerEvent) { - super.handleEvent(event); - - const { _dragging, _pointerRatio, requestVolumeChange } = super.getState() as VolumeSliderState; - - if (_dragging) { - requestVolumeChange(_pointerRatio); - } - } - - #handlePointerUp(event: PointerEvent) { - const { _dragging, _pointerRatio, requestVolumeChange } = super.getState() as VolumeSliderState; - - if (_dragging) { - requestVolumeChange(_pointerRatio); - } - - super.handleEvent(event); - } - - #handleKeyDown(event: KeyboardEvent) { - super.handleEvent(event); - - const { _pointerRatio, requestVolumeChange } = super.getState() as VolumeSliderState; - requestVolumeChange(_pointerRatio); - } -} - -function formatVolume(volume: number): string { - return `${Math.round(volume * 100)}%`; -} diff --git a/packages/__tech-preview__/core/src/index.ts b/packages/__tech-preview__/core/src/index.ts deleted file mode 100644 index 2709088f..00000000 --- a/packages/__tech-preview__/core/src/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { Popover, type PopoverState } from './components/popover'; -export { TimeSlider, type TimeSliderState } from './components/time-slider'; -export { Tooltip, type TooltipState } from './components/tooltip'; -export { VolumeSlider, type VolumeSliderState } from './components/volume-slider'; diff --git a/packages/__tech-preview__/core/src/media/engine/hls-engine.ts b/packages/__tech-preview__/core/src/media/engine/hls-engine.ts deleted file mode 100644 index 70d93f7f..00000000 --- a/packages/__tech-preview__/core/src/media/engine/hls-engine.ts +++ /dev/null @@ -1,80 +0,0 @@ -import Hls from 'hls.js'; - -/** @TODO Move interface/type defs (CJP) */ -/** @TODO Narrow and/or parameterize EventTarget via generics/inference (CJP) */ -export interface PlaybackEngine extends EventTarget { - src: HTMLMediaElement['src'] | undefined; - // NOTE: Unlike other APIs, this *must* be an HTMLMediaElement based on internal and/or external API assumptions (CJP) - mediaElement: HTMLMediaElement | undefined; - /** @TODO Widen/extend and/or parameterize EventTarget via generics/inference (CJP) */ - element: HTMLElement | undefined; - destroy: () => void; -} - -export class HlsPlaybackEngine extends EventTarget { - constructor() { - super(); - } - - protected _hlsInstance: Hls | undefined; - - get src(): string | undefined { - return this._hlsInstance?.url ?? undefined; - } - - set src(src: string | undefined) { - if (this.src === src) return; - - if (!this._hlsInstance) { - this._createHlsInstance(); - } - - if (!this.src && src) { - this._hlsInstance?.loadSource(src); - } - } - - // NOTE: Some playback engines may not rely on an underlying HTMLMediaElement but still have - // an HTMLElement (e.g. HTMLCanvasElement, HTMLImageElement, etc.). This API provides a more - // generic interface for cases where we want more minimal assumptions (e.g. fullscreen behaviors for most playtforms but not iOS) - get element(): HTMLElement | undefined { - return this.mediaElement; - } - - set element(val) { - if (this.element === val) return; - /** @TODO programmatic type checking? (CJP) */ - this.mediaElement = val as HTMLMediaElement; - } - - get mediaElement(): HTMLMediaElement | undefined { - return this._hlsInstance?.media ?? undefined; - } - - set mediaElement(val) { - if (this.mediaElement === val) return; - - if (!this._hlsInstance) { - this._createHlsInstance(); - } - - if (!this.mediaElement && val) { - this._hlsInstance?.attachMedia(val); - } - } - - protected _createHlsInstance(): void { - this._hlsInstance = new Hls(); - } - - protected _destroyHlsInstance(): void { - this._hlsInstance?.destroy(); - this._hlsInstance = undefined; - } - - destroy(): void { - this._destroyHlsInstance(); - } -} - -export const createHlsPlaybackEngine = (): PlaybackEngine => new HlsPlaybackEngine(); diff --git a/packages/__tech-preview__/core/src/media/engine/index.ts b/packages/__tech-preview__/core/src/media/engine/index.ts deleted file mode 100644 index a2bef9fa..00000000 --- a/packages/__tech-preview__/core/src/media/engine/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export type { PlaybackEngine as IBasePlaybackEngine } from './hls-engine'; -export { createHlsPlaybackEngine, HlsPlaybackEngine } from './hls-engine'; diff --git a/packages/__tech-preview__/core/src/media/index.ts b/packages/__tech-preview__/core/src/media/index.ts deleted file mode 100644 index 7ae7feee..00000000 --- a/packages/__tech-preview__/core/src/media/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './engine'; -export type { AudibleMediaStateOwner, MediaStateOwner, PlayableMediaStateOwner } from './playable'; -export { createMediaPlaybackController, Events, MediaPlaybackController } from './playable'; diff --git a/packages/__tech-preview__/core/src/media/playable.ts b/packages/__tech-preview__/core/src/media/playable.ts deleted file mode 100644 index 9bc3002b..00000000 --- a/packages/__tech-preview__/core/src/media/playable.ts +++ /dev/null @@ -1,138 +0,0 @@ -import type { PlaybackEngine } from '@/media/engine/hls-engine'; -import { createHlsPlaybackEngine } from '@/media/engine/hls-engine'; - -/** @TODO Split out "playable" vs. "audible" vs. "temporal" and compose via factory (current mixin pattern or spreadable mixin pattern) (CJP) */ -export const Events = [ - 'volumechange', - 'pause', - 'play', - 'playing', - 'emptied', - 'loadedmetadata', - 'timeupdate', - 'durationchange', -] as const; - -export interface MediaStateOwner extends EventTarget, Pick { - mediaElement?: HTMLMediaElement | undefined; -} - -export interface PlayableMediaStateOwner - extends EventTarget, - MediaStateOwner, - Pick {} - -export interface AudibleMediaStateOwner - extends EventTarget, - MediaStateOwner, - Pick {} - -export interface TemporalMediaStateOwner - extends EventTarget, - MediaStateOwner, - Pick {} - -export class MediaPlaybackController extends EventTarget implements MediaStateOwner, PlayableMediaStateOwner, AudibleMediaStateOwner { - protected _playbackEngine: PlaybackEngine; - - constructor() { - super(); - this._playbackEngine = createHlsPlaybackEngine(); - } - - get mediaElement(): HTMLMediaElement | undefined { - return this._playbackEngine.mediaElement; - } - - set mediaElement(value) { - if (this.mediaElement === value) return; - - if (this.mediaElement != null) { - Events.forEach((eventType) => { - this.mediaElement?.removeEventListener(eventType, this); - }); - } - - this._playbackEngine.mediaElement = value; - - if (this.mediaElement) { - Events.forEach((eventType) => { - this.mediaElement?.addEventListener(eventType, this); - }); - } - } - - get paused(): boolean { - return this.mediaElement?.paused ?? false; - } - - pause(): void { - /** @TODO implement deferred state etc. for cases where media has yet to be set */ - if (!this.mediaElement) return; - return this.mediaElement.pause(); - } - - play(): Promise { - /** @TODO implement deferred state etc. for cases where media has yet to be set */ - if (!this.mediaElement) throw new Error('No media element is set'); - return this.mediaElement.play(); - } - - get muted(): boolean { - return this.mediaElement?.muted ?? false; - } - - set muted(value: boolean) { - if (value === this.muted) return; - /** @TODO implement deferred state etc. for cases where media has yet to be set */ - if (!this.mediaElement) return; - this.mediaElement.muted = value; - } - - get volume(): number { - return this.mediaElement?.volume ?? 0; - } - - set volume(value: number) { - if (value === this.volume) return; - /** @TODO implement deferred state etc. for cases where media has yet to be set */ - if (!this.mediaElement) return; - this.mediaElement.volume = value; - } - - get duration(): number { - return this.mediaElement?.duration ?? 0; - } - - get currentTime(): number { - return this.mediaElement?.currentTime ?? 0; - } - - set currentTime(value: number) { - if (value === this.currentTime) return; - /** @TODO implement deferred state etc. for cases where media has yet to be set */ - if (!this.mediaElement) return; - this.mediaElement.currentTime = value; - } - - get src(): string { - return this._playbackEngine.src ?? ''; - } - - set src(value: string) { - this._playbackEngine.src = value; - } - - handleEvent(event: Event): void { - if (event.target === this.mediaElement) { - const clonedEvent = new (event.constructor as (typeof globalThis)['Event'])(event.type, event); - this.dispatchEvent(clonedEvent); - } - } - - destroy(): void { - this._playbackEngine.destroy(); - } -} - -export const createMediaPlaybackController = (): MediaPlaybackController => new MediaPlaybackController(); diff --git a/packages/__tech-preview__/core/src/store/definitions/current-time-display.ts b/packages/__tech-preview__/core/src/store/definitions/current-time-display.ts deleted file mode 100644 index 9c478cad..00000000 --- a/packages/__tech-preview__/core/src/store/definitions/current-time-display.ts +++ /dev/null @@ -1,27 +0,0 @@ -export interface CurrentTimeDisplayState { - /** The current time value in seconds */ - currentTime: number | undefined; - /** The total duration in seconds (for future functionality) */ - duration: number | undefined; -} - -export const currentTimeDisplayStateDefinition = { - keys: ['currentTime', 'duration'] as const, - stateTransform: (rawState: Record): CurrentTimeDisplayState => { - const { currentTime, duration } = rawState; - - return { - currentTime, - duration, - }; - }, - createRequestMethods: (_dispatch: (action: { type: string; detail?: any }) => void) => ({}), -}; - -export type CurrentTimeDisplayStateDefinition = typeof currentTimeDisplayStateDefinition; - -export type CurrentTimeDisplayComponentState = ReturnType; - -export type CurrentTimeDisplayRequestMethods = ReturnType< - typeof currentTimeDisplayStateDefinition.createRequestMethods ->; diff --git a/packages/__tech-preview__/core/src/store/definitions/duration-display.ts b/packages/__tech-preview__/core/src/store/definitions/duration-display.ts deleted file mode 100644 index a15b04d1..00000000 --- a/packages/__tech-preview__/core/src/store/definitions/duration-display.ts +++ /dev/null @@ -1,24 +0,0 @@ - -export interface DurationDisplayState { - /** The raw duration value in seconds */ - duration: number | undefined; -} - - -export const durationDisplayStateDefinition = { - keys: ['duration'] as const, - stateTransform: (rawState: Record): DurationDisplayState => { - const { duration } = rawState; - - return { - duration, - }; - }, - createRequestMethods: (_dispatch: (action: { type: string; detail?: any }) => void) => ({}), -}; - -export type DurationDisplayStateDefinition = typeof durationDisplayStateDefinition; - -export type DurationDisplayComponentState = ReturnType; - -export type DurationDisplayRequestMethods = ReturnType; diff --git a/packages/__tech-preview__/core/src/store/definitions/fullscreen-button.ts b/packages/__tech-preview__/core/src/store/definitions/fullscreen-button.ts deleted file mode 100644 index cdd7a8f7..00000000 --- a/packages/__tech-preview__/core/src/store/definitions/fullscreen-button.ts +++ /dev/null @@ -1,25 +0,0 @@ -export interface FullscreenButtonState { - fullscreen: boolean; -} - -export interface FullscreenButtonMethods { - requestEnterFullscreen: () => void; - requestExitFullscreen: () => void; -} - -export interface FullscreenButtonStateDefinition { - keys: string[]; - stateTransform: (rawState: any) => FullscreenButtonState; - createRequestMethods: (dispatch: (action: { type: string; detail?: any }) => void) => FullscreenButtonMethods; -} - -export const fullscreenButtonStateDefinition: FullscreenButtonStateDefinition = { - keys: ['fullscreen'], - stateTransform: (rawState: any): FullscreenButtonState => ({ - fullscreen: rawState.fullscreen ?? false, - }), - createRequestMethods: (dispatch): FullscreenButtonMethods => ({ - requestEnterFullscreen: () => dispatch({ type: 'fullscreenrequest', detail: true }), - requestExitFullscreen: () => dispatch({ type: 'fullscreenrequest', detail: false }), - }), -}; diff --git a/packages/__tech-preview__/core/src/store/definitions/mute-button.ts b/packages/__tech-preview__/core/src/store/definitions/mute-button.ts deleted file mode 100644 index 5aa111ff..00000000 --- a/packages/__tech-preview__/core/src/store/definitions/mute-button.ts +++ /dev/null @@ -1,29 +0,0 @@ -export interface MuteButtonState { - muted: boolean; - volumeLevel: string; -} - -export interface MuteButtonMethods { - requestMute: () => void; - requestUnmute: () => void; -} - -export interface MuteButtonStateDefinition { - keys: string[]; - stateTransform: (rawState: any) => MuteButtonState; - createRequestMethods: (dispatch: (action: { type: string }) => void) => MuteButtonMethods; -} - -export const muteButtonStateDefinition: MuteButtonStateDefinition = { - keys: ['muted', 'volumeLevel'], - - stateTransform: (rawState: any): MuteButtonState => ({ - muted: rawState.muted ?? false, - volumeLevel: rawState.volumeLevel ?? 'off', - }), - - createRequestMethods: (dispatch): MuteButtonMethods => ({ - requestMute: () => dispatch({ type: 'muterequest' }), - requestUnmute: () => dispatch({ type: 'unmuterequest' }), - }), -}; diff --git a/packages/__tech-preview__/core/src/store/definitions/play-button.ts b/packages/__tech-preview__/core/src/store/definitions/play-button.ts deleted file mode 100644 index 69decf63..00000000 --- a/packages/__tech-preview__/core/src/store/definitions/play-button.ts +++ /dev/null @@ -1,27 +0,0 @@ -export interface PlayButtonState { - paused: boolean; -} - -export interface PlayButtonMethods { - requestPlay: () => void; - requestPause: () => void; -} - -export interface PlayButtonStateDefinition { - keys: string[]; - stateTransform: (rawState: any) => PlayButtonState; - createRequestMethods: (dispatch: (action: { type: string; detail?: unknown }) => void) => PlayButtonMethods; -} - -export const playButtonStateDefinition: PlayButtonStateDefinition = { - keys: ['paused'], - - stateTransform: (rawState: any): PlayButtonState => ({ - paused: rawState.paused ?? true, - }), - - createRequestMethods: (dispatch): PlayButtonMethods => ({ - requestPlay: () => dispatch({ type: 'playrequest' }), - requestPause: () => dispatch({ type: 'pauserequest' }), - }), -}; diff --git a/packages/__tech-preview__/core/src/store/definitions/preview-time-display.ts b/packages/__tech-preview__/core/src/store/definitions/preview-time-display.ts deleted file mode 100644 index b85e889c..00000000 --- a/packages/__tech-preview__/core/src/store/definitions/preview-time-display.ts +++ /dev/null @@ -1,16 +0,0 @@ -export interface PreviewTimeDisplayState { - /** The preview time value in seconds */ - previewTime: number | undefined; -} - -export interface PreviewTimeDisplayStateDefinition { - keys: (keyof PreviewTimeDisplayState)[]; - stateTransform: (rawState: any) => PreviewTimeDisplayState; -} - -export const previewTimeDisplayStateDefinition: PreviewTimeDisplayStateDefinition = { - keys: ['previewTime'], - stateTransform: (rawState: any) => ({ - previewTime: rawState.previewTime ?? 0, - }), -}; diff --git a/packages/__tech-preview__/core/src/store/definitions/time-slider.ts b/packages/__tech-preview__/core/src/store/definitions/time-slider.ts deleted file mode 100644 index d920cead..00000000 --- a/packages/__tech-preview__/core/src/store/definitions/time-slider.ts +++ /dev/null @@ -1,33 +0,0 @@ -export interface TimeSliderState { - currentTime: number; - duration: number; - previewTime: number; -} - -export interface TimeSliderMethods { - requestSeek: (time: number) => void; - requestPreview: (time: number) => void; -} - -export interface TimeSliderStateDefinition { - keys: string[]; - stateTransform: (rawState: any) => TimeSliderState; - createRequestMethods: (dispatch: (action: { type: string; detail?: any }) => void) => TimeSliderMethods; -} - -export const timeSliderStateDefinition: TimeSliderStateDefinition = { - keys: ['currentTime', 'duration', 'previewTime'], - stateTransform: (rawState: any) => ({ - currentTime: rawState.currentTime ?? 0, - duration: rawState.duration ?? 0, - previewTime: rawState.previewTime ?? 0, - }), - createRequestMethods: dispatch => ({ - requestSeek: (time: number) => { - dispatch({ type: 'seekrequest', detail: time }); - }, - requestPreview: (time: number) => { - dispatch({ type: 'previewrequest', detail: time }); - }, - }), -}; diff --git a/packages/__tech-preview__/core/src/store/definitions/volume-slider.ts b/packages/__tech-preview__/core/src/store/definitions/volume-slider.ts deleted file mode 100644 index e5ed0390..00000000 --- a/packages/__tech-preview__/core/src/store/definitions/volume-slider.ts +++ /dev/null @@ -1,38 +0,0 @@ -export interface VolumeSliderState { - volume: number; - muted: boolean; - volumeLevel: 'high' | 'medium' | 'low' | 'off'; -} - -export interface VolumeSliderMethods { - requestVolumeChange: (volume: number) => void; -} - -export interface VolumeSliderStateDefinition { - keys: string[]; - stateTransform: (rawState: any) => VolumeSliderState; - createRequestMethods: (dispatch: (action: { type: string; detail?: any }) => void) => VolumeSliderMethods; -} - -export const volumeSliderStateDefinition: VolumeSliderStateDefinition = { - keys: ['volume', 'muted', 'volumeLevel'], - stateTransform: (rawState: any) => ({ - volume: rawState.volume ?? 1, - muted: rawState.muted ?? false, - volumeLevel: rawState.volumeLevel ?? 'high', - }), - createRequestMethods: dispatch => ({ - /** - * @TODO Unmuting is owned by the "request-map" in media-chrome. - * The closest equivalent to that is the "actions" in the current architecture. - * Should unmuting live here (even if "here" gets promoted to the state model) or "actions" or state setter? - * Currently this is solved in the state setter (as is the corresponding unmute behavior). See mediators/audible for details. (CJP) - */ - requestVolumeChange: (volume: number) => { - // if (volume > 0) { - // dispatch({ type: 'unmuterequest' }); - // } - dispatch({ type: 'volumerequest', detail: volume }); - }, - }), -}; diff --git a/packages/__tech-preview__/core/src/store/factory.ts b/packages/__tech-preview__/core/src/store/factory.ts deleted file mode 100644 index 2d1c7752..00000000 --- a/packages/__tech-preview__/core/src/store/factory.ts +++ /dev/null @@ -1,168 +0,0 @@ -import type { StateOwners } from './types'; - -import { getKey, map, subscribeKeys } from 'nanostores'; - -export interface EventOrAction { - type: string; - detail?: D; - target?: EventTarget; -} - -export type FacadeGetter = (stateOwners: StateOwners, event?: EventOrAction) => T; - -export type FacadeSetter = (value: T, stateOwners: StateOwners) => void; - -export type StateOwnerUpdateHandler = ( - handler: (value?: T) => void, - stateOwners: StateOwners -) => (() => void) | void; - -export interface ReadonlyFacadeProp { - get: FacadeGetter; - stateOwnersUpdateHandlers?: StateOwnerUpdateHandler[]; -} - -export type FacadeProp = ReadonlyFacadeProp & { - set: FacadeSetter; - /** @TODO We probably need to refactor this for more complex cases where we can't simply translate to a setter */ - actions: { - [k: string]: (val: { type: string; detail?: unknown }) => ReturnType>; - }; -}; - -export interface StateMediator { - paused: FacadeProp; - muted: FacadeProp; - volume: FacadeProp; - volumeLevel: ReadonlyFacadeProp<'high' | 'medium' | 'low' | 'off'>; - currentTime: FacadeProp; - duration: ReadonlyFacadeProp; - seekable: ReadonlyFacadeProp<[number, number] | undefined>; - previewTime: FacadeProp; - fullscreen: FacadeProp; -} - -export interface MediaStore { - dispatch: (action: { type: string; detail?: unknown }) => void; - getState: () => any; - getKeys: (keys: string[]) => Record; - subscribeKeys: (keys: string[], callback: (state: any) => void) => void; - subscribe: (callback: (state: any) => void) => void; -} - -export function createMediaStore({ - // media, - stateMediator, -}: { - media?: any; - container?: any; - stateMediator: Partial; -}): MediaStore { - const stateOwners: StateOwners = {}; - const store = map({}); - const stateUpdateHandlerCleanups: Record void)[]> = {}; - const keys = Object.keys(stateMediator); - - function updateStateOwners(nextStateOwners: any) { - // Check if any state owner has changed - const hasChanges = Object.entries(nextStateOwners).some( - ([key, value]) => stateOwners[key as keyof StateOwners] !== value, - ); - - if (!hasChanges) { - return; - } - - // Clean up existing handlers - Object.entries(stateUpdateHandlerCleanups).forEach(([stateName, cleanups]) => { - cleanups.forEach(cleanup => cleanup?.()); - stateUpdateHandlerCleanups[stateName] = []; - }); - - Object.assign(stateOwners, nextStateOwners); - store.set(getInitialState(stateMediator, stateOwners)); - - // Set up new handlers - Object.entries(stateMediator).forEach(([stateName, stateObject]) => { - const { get, stateOwnersUpdateHandlers = [] } = stateObject; - - if (!stateUpdateHandlerCleanups[stateName]) { - stateUpdateHandlerCleanups[stateName] = []; - } - - // Create handler that updates the store - const updateHandler = (value?: any) => { - const nextValue = value !== undefined ? value : get(stateOwners); - store.setKey(stateName, nextValue); - }; - - // Execute each stateOwnersUpdateHandler - stateOwnersUpdateHandlers.forEach((setupHandler) => { - const cleanup = setupHandler(updateHandler, stateOwners); - if (typeof cleanup === 'function') { - stateUpdateHandlerCleanups[stateName]?.push(cleanup); - } - }); - }); - } - - return { - dispatch(action: Pick, 'type' | 'detail'>): void { - const { type, detail } = action; - - if (type === 'mediastateownerchangerequest') { - updateStateOwners({ media: detail }); - } else if (type === 'containerstateownerchangerequest') { - updateStateOwners({ container: detail }); - } else { - Object.entries(stateMediator).forEach(([stateName, stateObject]) => { - const { set, actions } = stateObject as FacadeProp; - - if (actions?.[type]) { - const actionFn = actions[type]; - const actionValue = actionFn(action); - if (set) { - (set as FacadeSetter)(actionValue, stateOwners); - } else { - // If there is no setter, store the action value in the store. - // Might need revisiting. - store.setKey(stateName, actionValue); - } - } - }); - } - }, - - getState(): any { - return store.get(); - }, - - getKeys(keys: string[]): Record { - return keys.reduce( - (acc, k) => { - acc[k] = getKey(store, k); - return acc; - }, - {} as { [k: string]: any }, - ); - }, - - subscribeKeys(keys: string[], callback: (state: any) => void): void { - subscribeKeys(store, keys, callback); - }, - - // NOTE: In the POC architecture using nano-stores, subscribe is simply subscribeKeys across all keys. (CJP) - subscribe(callback: (state: any) => void): void { - subscribeKeys(store, keys, callback); - }, - }; -} - -function getInitialState(stateMediator: Partial, stateOwners: any) { - const initialState: any = {}; - for (const [stateName, { get }] of Object.entries(stateMediator)) { - if (!get) continue; - initialState[stateName] = get(stateOwners); - } - return initialState; -} diff --git a/packages/__tech-preview__/core/src/store/index.ts b/packages/__tech-preview__/core/src/store/index.ts deleted file mode 100644 index 64f86157..00000000 --- a/packages/__tech-preview__/core/src/store/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -export * from './definitions/current-time-display'; -export * from './definitions/duration-display'; -export * from './definitions/fullscreen-button'; -export * from './definitions/mute-button'; -export * from './definitions/play-button'; -export * from './definitions/preview-time-display'; -export * from './definitions/time-slider'; -export * from './definitions/volume-slider'; - -export * from './factory'; -export { createMediaStore } from './media-store'; - -export * from './mediators/audible'; -export * from './mediators/playable'; -export * from './mediators/temporal'; diff --git a/packages/__tech-preview__/core/src/store/media-store.ts b/packages/__tech-preview__/core/src/store/media-store.ts deleted file mode 100644 index 98769a61..00000000 --- a/packages/__tech-preview__/core/src/store/media-store.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { MediaStore } from './factory'; - -import { createMediaStore as factory } from './factory'; -import { audible } from './mediators/audible'; -import { fullscreenable } from './mediators/fullscreenable'; -import { playable } from './mediators/playable'; -import { preview } from './mediators/preview'; -import { temporal } from './mediators/temporal'; - -// Example of default media store with default state mediator definitions. (CJP) -// NOTE: We can also change the API to take an array of stateMediators (or either/both) (CJP) -const stateMediator = { ...playable, ...audible, ...temporal, ...fullscreenable, ...preview }; - -type Params = Partial[0]>; - -export function createMediaStore(params: Params = {}): MediaStore { - return factory({ - stateMediator: stateMediator as any, - ...params, - }); -} diff --git a/packages/__tech-preview__/core/src/store/mediators/audible.ts b/packages/__tech-preview__/core/src/store/mediators/audible.ts deleted file mode 100644 index ff6fcb47..00000000 --- a/packages/__tech-preview__/core/src/store/mediators/audible.ts +++ /dev/null @@ -1,87 +0,0 @@ -import type { StateOwners } from '../types'; - -export const audible = { - muted: { - get(stateOwners: StateOwners): boolean { - const { media } = stateOwners; - return media?.muted ?? false; - }, - set(value: boolean, stateOwners: StateOwners): void { - const { media } = stateOwners; - if (!media) return; - media.muted = value; - if (!value && !media.volume) { - media.volume = 0.25; - } - }, - stateOwnersUpdateHandlers: [ - (handler: (value?: boolean) => void, stateOwners: StateOwners): (() => void) | void => { - const { media } = stateOwners; - if (!media) return; - - const eventHandler = () => handler(); - media.addEventListener('volumechange', eventHandler); - - return () => media.removeEventListener('volumechange', eventHandler); - }, - ] as const, - actions: { - /** @TODO Refactor me to play more nicely with side effects that don't/can't correlate with set() API or aren't simple 1:1 with getter vs. setter (CJP) */ - muterequest: () => true, - unmuterequest: () => false, - }, - }, - volume: { - get(stateOwners: StateOwners): number { - const { media } = stateOwners; - return media?.volume ?? 1.0; - }, - set(value: number, stateOwners: StateOwners): void { - const { media } = stateOwners; - if (!media) return; - const numericValue = +value; - if (!Number.isFinite(numericValue)) return; - media.volume = numericValue; - if (numericValue > 0) { - media.muted = false; - } - }, - stateOwnersUpdateHandlers: [ - (handler: (value?: number) => void, stateOwners: StateOwners): (() => void) | void => { - const { media } = stateOwners; - if (!media) return; - - const eventHandler = () => handler(); - media.addEventListener('volumechange', eventHandler); - - return () => media.removeEventListener('volumechange', eventHandler); - }, - ] as const, - actions: { - /** @TODO Refactor me to play more nicely with side effects that don't/can't correlate with set() API (CJP) */ - volumerequest: ({ detail }: Pick, 'detail'> = { detail: 0 }): number => +detail, - }, - }, - // NOTE: This could be (re)implemented as "derived state" in some manner (e.g. selectors but also other patterns/conventions) if preferred. (CJP) - volumeLevel: { - get(stateOwners: StateOwners): 'high' | 'medium' | 'low' | 'off' { - const { media } = stateOwners; - if (typeof media?.volume == 'undefined') return 'high'; - if (media.muted || media.volume === 0) return 'off'; - if (media.volume < 0.5) return 'low'; - if (media.volume < 0.75) return 'medium'; - return 'high'; - }, - stateOwnersUpdateHandlers: [ - (handler: (value?: 'high' | 'medium' | 'low' | 'off') => void, stateOwners: any): void | (() => void) => { - const { media } = stateOwners; - if (!media) return; - - const eventHandler = () => handler(); - media.addEventListener('volumechange', eventHandler); - - return () => media.removeEventListener('volumechange', eventHandler); - }, - ] as const, - }, -}; diff --git a/packages/__tech-preview__/core/src/store/mediators/fullscreenable.ts b/packages/__tech-preview__/core/src/store/mediators/fullscreenable.ts deleted file mode 100644 index c9893057..00000000 --- a/packages/__tech-preview__/core/src/store/mediators/fullscreenable.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { containsComposedNode } from '@videojs/utils-preview/dom'; - -/** @TODO This is implemented for web/browser only! We will need an alternative state mediator model for e.g. React Native. (CJP) */ -export const fullscreenable = { - fullscreen: { - get(stateOwners: any): boolean { - const { container } = stateOwners; - if (!container || !globalThis?.document) return false; - - const doc = globalThis.document; - const currentFullscreenElement - = doc.fullscreenElement - || (doc as any).webkitFullscreenElement - || (doc as any).mozFullScreenElement - || (doc as any).msFullscreenElement; - - if (!currentFullscreenElement) return false; - - // If document.fullscreenElement is the container, we're definitely in fullscreen - if (currentFullscreenElement === container) { - return true; - } - - // Check if container is contained within the fullscreen element - if (currentFullscreenElement.contains?.(container)) { - return true; - } - - // Handle web components with shadow DOM - traverse shadow DOM layers - // In this case (most modern browsers), the fullscreenElement may be - // a web component that contains our container within shadow DOM layers - if (currentFullscreenElement.localName?.includes('-')) { - let currentRoot = currentFullscreenElement.shadowRoot; - - // Check if ShadowRoot supports fullscreenElement (Safari < 16.4 workaround) - const fullscreenElementKey - = 'fullscreenElement' in doc - ? 'fullscreenElement' - : 'webkitFullscreenElement' in doc - ? 'webkitFullscreenElement' - : undefined; - - if (fullscreenElementKey && !(fullscreenElementKey in (currentRoot || {}))) { - // For older Safari versions, use composed node containment check - return containsComposedNode(currentFullscreenElement, container); - } - - // Traverse shadow DOM layers looking for our container - if (fullscreenElementKey) { - while (currentRoot?.[fullscreenElementKey]) { - if (currentRoot[fullscreenElementKey] === container) return true; - if (currentRoot[fullscreenElementKey]?.contains?.(container)) return true; - currentRoot = currentRoot[fullscreenElementKey]?.shadowRoot; - } - } - } - - return false; - }, - set(value: boolean, stateOwners: any): void { - const { container, media } = stateOwners; - if (!container || !media || !globalThis?.document) return; - - try { - if (value) { - // Enter fullscreen - if (container.requestFullscreen) { - container.requestFullscreen(); - } else if (media._playbackEngine?.element?.webkitEnterFullscreen) { - // Safari support (IOS) - media._playbackEngine.element.webkitEnterFullscreen(); - } else if (container.webkitRequestFullscreen) { - // Safari support (non IOS) - container.webkitRequestFullscreen(); - } else if (container.mozRequestFullScreen) { - // Firefox support - container.mozRequestFullScreen(); - } else if (container.msRequestFullscreen) { - // IE/Edge support - container.msRequestFullscreen(); - } - } else { - // Exit fullscreen - const doc = globalThis.document as any; - if (doc.exitFullscreen) { - doc.exitFullscreen(); - } else if (doc.webkitExitFullscreen) { - // Safari support - doc.webkitExitFullscreen(); - } else if (doc.mozCancelFullScreen) { - // Firefox support - doc.mozCancelFullScreen(); - } else if (doc.msExitFullscreen) { - // IE/Edge support - doc.msExitFullscreen(); - } - } - } catch (error) { - // Gracefully handle fullscreen API errors (e.g., user interaction required) - console.warn('Fullscreen operation failed:', error); - } - }, - stateOwnersUpdateHandlers: [ - (handler: (value?: boolean) => void, _stateOwners: any): void | (() => void) => { - if (!globalThis?.document) return; - - const eventHandler = () => handler(); - const events = ['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'MSFullscreenChange']; - - events.forEach((event) => { - globalThis.document.addEventListener(event, eventHandler); - }); - - return () => { - events.forEach((event) => { - globalThis.document.removeEventListener(event, eventHandler); - }); - }; - }, - ] as const, - actions: { - /** Toggle fullscreen state or explicitly enter/exit based on detail */ - fullscreenrequest: ({ detail }: Pick, 'detail'> = { detail: undefined }): boolean => { - // If detail is provided, use it; otherwise toggle current state - if (typeof detail === 'boolean') { - return detail; - } - - // Toggle behavior: check current fullscreen state - const fullscreenEl = globalThis?.document?.fullscreenElement; - return !fullscreenEl; - }, - }, - }, -}; diff --git a/packages/__tech-preview__/core/src/store/mediators/index.ts b/packages/__tech-preview__/core/src/store/mediators/index.ts deleted file mode 100644 index 92c41218..00000000 --- a/packages/__tech-preview__/core/src/store/mediators/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { audible } from './audible'; -export { fullscreenable } from './fullscreenable'; -export { playable } from './playable'; -export { preview } from './preview'; -export { temporal } from './temporal'; diff --git a/packages/__tech-preview__/core/src/store/mediators/playable.ts b/packages/__tech-preview__/core/src/store/mediators/playable.ts deleted file mode 100644 index b44a626b..00000000 --- a/packages/__tech-preview__/core/src/store/mediators/playable.ts +++ /dev/null @@ -1,29 +0,0 @@ -export const playable = { - paused: { - get(stateOwners: any): boolean { - const { media } = stateOwners; - return media?.paused ?? true; - }, - set(value: boolean, stateOwners: any): void { - const { media } = stateOwners; - media?.[value ? 'pause' : 'play'](); - }, - stateOwnersUpdateHandlers: [ - (handler: (value?: boolean) => void, stateOwners: any): void | (() => void) => { - const { media } = stateOwners; - if (!media) return; - - const eventHandler = () => handler(); - const events = ['play', 'playing', 'pause', 'emptied']; - events.forEach(event => media.addEventListener(event, eventHandler)); - - return () => events.forEach(event => media.removeEventListener(event, eventHandler)); - }, - ] as const, - actions: { - /** @TODO Refactor me to play more nicely with side effects that don't/can't correlate with set() API (CJP) */ - playrequest: () => false, - pauserequest: () => true, - }, - }, -}; diff --git a/packages/__tech-preview__/core/src/store/mediators/preview.ts b/packages/__tech-preview__/core/src/store/mediators/preview.ts deleted file mode 100644 index e0958275..00000000 --- a/packages/__tech-preview__/core/src/store/mediators/preview.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const preview = { - previewTime: { - actions: { - previewrequest: ({ detail }: Pick, 'detail'> = { detail: 0 }): number => +detail, - }, - }, -}; diff --git a/packages/__tech-preview__/core/src/store/mediators/temporal.ts b/packages/__tech-preview__/core/src/store/mediators/temporal.ts deleted file mode 100644 index def18357..00000000 --- a/packages/__tech-preview__/core/src/store/mediators/temporal.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { isValidNumber } from '@videojs/utils-preview'; - -export const temporal = { - currentTime: { - get(stateOwners: any): number { - const { media } = stateOwners; - return media?.currentTime ?? 0; - }, - set(value: number, stateOwners: any): void { - const { media } = stateOwners; - if (!media || !isValidNumber(value)) return; - media.currentTime = value; - }, - stateOwnersUpdateHandlers: [ - (handler: (value?: number) => void, stateOwners: any): void | (() => void) => { - const { media } = stateOwners; - if (!media) return; - - const eventHandler = () => handler(); - const events = ['timeupdate', 'loadedmetadata']; - events.forEach(event => media.addEventListener(event, eventHandler)); - - return () => events.forEach(event => media.removeEventListener(event, eventHandler)); - }, - ] as const, - actions: { - /** @TODO Support more sophisticated seeking patterns like seek-to-live, relative seeking, etc. (CJP) */ - seekrequest: ({ detail }: Pick, 'detail'> = { detail: 0 }): number => +detail, - }, - }, - - duration: { - get(stateOwners: any): number { - const { media } = stateOwners; - - // Return 0 if no media or invalid duration - if (!media?.duration || Number.isNaN(media.duration) || !Number.isFinite(media.duration)) { - return 0; - } - - return media.duration; - }, - stateOwnersUpdateHandlers: [ - (handler: (value?: number) => void, stateOwners: any): void | (() => void) => { - const { media } = stateOwners; - if (!media) return; - - const eventHandler = () => handler(); - const events = ['loadedmetadata', 'durationchange', 'emptied']; - events.forEach(event => media.addEventListener(event, eventHandler)); - - return () => events.forEach(event => media.removeEventListener(event, eventHandler)); - }, - ] as const, - }, - - seekable: { - get(stateOwners: any): [number, number] | undefined { - const { media } = stateOwners; - - if (!media?.seekable?.length) return undefined; - - const start = media.seekable.start(0); - const end = media.seekable.end(media.seekable.length - 1); - - // Account for cases where metadata has an "empty" seekable range - if (!start && !end) return undefined; - - return [Number(start.toFixed(3)), Number(end.toFixed(3))]; - }, - stateOwnersUpdateHandlers: [ - (handler: (value?: [number, number] | undefined) => void, stateOwners: any): void | (() => void) => { - const { media } = stateOwners; - if (!media) return; - - const eventHandler = () => handler(); - const events = ['loadedmetadata', 'emptied', 'progress', 'seekablechange']; - events.forEach(event => media.addEventListener(event, eventHandler)); - - return () => events.forEach(event => media.removeEventListener(event, eventHandler)); - }, - ] as const, - }, -}; diff --git a/packages/__tech-preview__/core/src/store/types.ts b/packages/__tech-preview__/core/src/store/types.ts deleted file mode 100644 index 9fec8287..00000000 --- a/packages/__tech-preview__/core/src/store/types.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface StateOwners { - media?: HTMLMediaElement; - container?: HTMLElement; -} diff --git a/packages/__tech-preview__/core/tsconfig.build.json b/packages/__tech-preview__/core/tsconfig.build.json deleted file mode 100644 index b90fc83e..00000000 --- a/packages/__tech-preview__/core/tsconfig.build.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["src"] -} diff --git a/packages/__tech-preview__/core/tsconfig.json b/packages/__tech-preview__/core/tsconfig.json deleted file mode 100644 index 7db81593..00000000 --- a/packages/__tech-preview__/core/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - // This will be removed. - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "baseUrl": ".", - "paths": { - "@/*": ["src/*"] - } - }, - "include": ["src"] -} diff --git a/packages/__tech-preview__/core/tsdown.config.ts b/packages/__tech-preview__/core/tsdown.config.ts deleted file mode 100644 index a796899a..00000000 --- a/packages/__tech-preview__/core/tsdown.config.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { defineConfig } from 'tsdown'; - -export default defineConfig({ - entry: { - index: './src/index.ts', - media: './src/media/index.ts', - store: './src/store/index.ts', - }, - platform: 'browser', - format: 'es', - sourcemap: true, - clean: true, - alias: { - '@': new URL('./src', import.meta.url).pathname, - }, - dts: { - oxc: true, - }, -}); diff --git a/packages/__tech-preview__/examples/html/frosted-eject.html b/packages/__tech-preview__/examples/html/frosted-eject.html deleted file mode 100644 index b24883a4..00000000 --- a/packages/__tech-preview__/examples/html/frosted-eject.html +++ /dev/null @@ -1,134 +0,0 @@ - - - - - - HTML Frosted Ejected - - - - - -

Frosted Skin (ejected)

- ← Back to index - - - - -
- -
- - - - - - Play - Pause - - -
- - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - Enter Fullscreen - Exit Fullscreen - -
-
-
- - - diff --git a/packages/__tech-preview__/examples/html/frosted.html b/packages/__tech-preview__/examples/html/frosted.html deleted file mode 100644 index f101da31..00000000 --- a/packages/__tech-preview__/examples/html/frosted.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - VJS HTML Demo - - - - - -

Frosted Skin

- ← Back to index -
- - - diff --git a/packages/__tech-preview__/examples/html/index.html b/packages/__tech-preview__/examples/html/index.html deleted file mode 100644 index da048e07..00000000 --- a/packages/__tech-preview__/examples/html/index.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - Video.js - HTML - - -

Video.js - HTML

- - - diff --git a/packages/__tech-preview__/examples/html/minimal-eject.html b/packages/__tech-preview__/examples/html/minimal-eject.html deleted file mode 100644 index 5707376b..00000000 --- a/packages/__tech-preview__/examples/html/minimal-eject.html +++ /dev/null @@ -1,141 +0,0 @@ - - - - - - HTML Minimal Ejected - - - - - -

Minimal Skin (ejected)

- ← Back to index - - - - -
- -
- - - - - - - - Play - Pause - - -
- - - - - / - - -
- - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - Enter Fullscreen - Exit Fullscreen - -
-
-
-
- - - diff --git a/packages/__tech-preview__/examples/html/minimal.html b/packages/__tech-preview__/examples/html/minimal.html deleted file mode 100644 index 28d865d4..00000000 --- a/packages/__tech-preview__/examples/html/minimal.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - VJS HTML Demo - - - - - -

Minimal Skin

- ← Back to index -
- - - diff --git a/packages/__tech-preview__/examples/html/package.json b/packages/__tech-preview__/examples/html/package.json deleted file mode 100644 index ddf4951c..00000000 --- a/packages/__tech-preview__/examples/html/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "html-demo", - "type": "module", - "version": "1.0.0", - "private": true, - "description": "HTML example for Video.js 10", - "scripts": { - "dev": "vite", - "build": "vite build", - "preview": "vite preview" - }, - "dependencies": { - "@videojs/html-preview": "workspace:*" - }, - "devDependencies": { - "typescript": "^5.9.3", - "vite": "^7.2.4" - } -} diff --git a/packages/__tech-preview__/examples/html/src/frosted-eject.css b/packages/__tech-preview__/examples/html/src/frosted-eject.css deleted file mode 100644 index 4ec8468b..00000000 --- a/packages/__tech-preview__/examples/html/src/frosted-eject.css +++ /dev/null @@ -1,359 +0,0 @@ -media-container * { - box-sizing: border-box; -} - -media-container { - position: relative; - isolation: isolate; - container: root / inline-size; - overflow: clip; - border-radius: var(--frosted-border-radius, 2rem); - background: oklab(0 0 0); - font-family: ui-sans-serif, system-ui, sans-serif; - font-size: 0.8125rem; - line-height: 1.5; - -webkit-font-smoothing: auto; - -moz-osx-font-smoothing: auto; -} -media-container::before, -media-container::after { - content: ''; - position: absolute; - pointer-events: none; - border-radius: inherit; - z-index: 10; -} -media-container::before { - inset: 1px; - box-shadow: inset 0 0 0 1px oklab(1 0 0 / 0.15); -} -media-container::after { - inset: 0; - box-shadow: inset 0 0 0 1px oklab(0 0 0 / 0.1); -} - -/* Fullscreen */ -media-container:fullscreen { - border-radius: 0; -} - -media-container > ::slotted([slot='media']), -media-container > video, -media-container > audio { - display: block; - width: 100%; - height: 100%; -} - -/* Media Container UI Overlay Styling */ -media-container > .overlay { - position: absolute; - inset: 0; - display: flex; - flex-flow: column nowrap; - align-items: start; - pointer-events: none; - border-radius: inherit; - background-image: linear-gradient(to top, oklab(0 0 0 / 0.5), oklab(0 0 0 / 0.3), rgba(0, 0, 0, 0)); - backdrop-filter: saturate(1.5) brightness(0.9); - transition: opacity 0.15s ease-out; - transition-duration: 100ms; - transition-delay: 0ms; -} -media-container[data-controls='hidden'] > .overlay { - transition-delay: 500ms; - transition-duration: 300ms; - opacity: 0; -} - -/* Common Surface Styles - e.g. tooltips, popovers, controls */ -.surface { - background-color: oklab(1 0 0 / 0.1); - backdrop-filter: blur(64px) brightness(0.9) saturate(1.5); - box-shadow: - inset 0 0 0 1px oklab(1 0 0 / 0.05), - 0 0 0 1px oklab(0 0 0 / 0.15), - oklab(0 0 0 / 0.15) 0px 1px 3px 0px, - oklab(0 0 0 / 0.15) 0px 1px 2px -1px; -} -@media (prefers-reduced-transparency: reduce) { - .surface { - background-color: oklab(0 0 0 / 0.7); - box-shadow: - inset 0 0 0 1px oklab(0 0 0), - 0 0 0 1px oklab(1 0 0 / 0.2); - } -} -@media (prefers-contrast: more) { - .surface { - background-color: oklab(0 0 0 / 0.9); - box-shadow: - inset 0 0 0 1px oklab(0 0 0), - 0 0 0 1px oklab(1 0 0 / 0.2); - } -} - -/* Media Control Bar UI/Styles */ -.control-bar { - position: absolute; - bottom: 0.75rem; - inset-inline: 0.75rem; - padding: 0.25rem; - display: flex; - align-items: center; - gap: 0.125rem; - border-radius: calc(infinity * 1px); - will-change: scale, transform, filter, opacity; - transition-property: scale, transform, filter, opacity; - transition-delay: 0ms; - transition-duration: 100ms; - transition-timing-function: ease-out; - transform-origin: bottom; - color: oklab(1 0 0); -} -media-container[data-controls='hidden'] > .control-bar { - opacity: 0; - scale: 0.9; - filter: blur(8px); - transition-delay: 500ms; - transition-duration: 300ms; - pointer-events: none; -} - -/* Time Display Styling */ -.time-controls { - display: flex; - align-items: center; - flex: 1; - gap: 0.75rem; - padding-inline: 0.375rem; -} -media-current-time-display, -media-duration-display { - text-shadow: 0 1px 0 oklab(0 0 0 / 0.25); - font-variant-numeric: tabular-nums; -} - -/* Generic Media Button Styling */ -.button { - display: grid; - flex-shrink: 0; - padding: 0.5rem; - cursor: pointer; - background: transparent; - border: none; - border-radius: calc(infinity * 1px); - color: oklab(1 0 0 / 0.9); - user-select: none; - outline: 2px solid transparent; - outline-offset: -2px; - transition-property: background-color, color, outline-offset; - transition-duration: 150ms; - transition-timing-function: ease-out; -} -.button:hover, -.button:focus-visible, -.button[aria-expanded='true'] { - background-color: oklab(1 0 0 / 0.1); - color: oklab(1 0 0); - text-decoration: none; -} -.button:focus-visible { - outline-color: oklch(62.3% 0.214 259.815); - outline-offset: 2px; -} -.button[disabled] { - cursor: not-allowed; - opacity: 0.5; - filter: grayscale(1); -} - -.button .icon { - grid-area: 1 / 1; - width: 18px; - height: 18px; - filter: drop-shadow(0 1px 0 oklab(0 0 0 / 0.25)); -} - -/* Media Play Button UI/Styles */ -media-play-button .icon { - opacity: 0; - transition: opacity 150ms linear; -} - -media-play-button:not([data-paused]) .pause-icon, -media-play-button[data-paused] .play-icon { - opacity: 1; -} - -/* Media Fullscreen Button UI/Styles */ -media-fullscreen-button .icon { - display: none; -} -media-fullscreen-button:not([data-fullscreen]) .fullscreen-enter-icon, -media-fullscreen-button[data-fullscreen] .fullscreen-exit-icon { - display: inline; -} - -/* One way to define the "default visible" icon (CJP) */ -media-mute-button .icon { - display: none; -} -media-mute-button:not([data-volume-level]) .volume-low-icon, -media-mute-button[data-volume-level='high'] .volume-high-icon, -media-mute-button[data-volume-level='medium'] .volume-high-icon, -media-mute-button[data-volume-level='low'] .volume-low-icon, -media-mute-button[data-volume-level='off'] .volume-off-icon { - display: inline; -} - -/* TimeSlider Component Styles */ -.slider { - flex: 1; - display: flex; - align-items: center; - justify-content: center; - position: relative; - border-radius: calc(infinity * 1px); - outline: none; -} - -/* Horizontal orientation styles */ -.slider[data-orientation='horizontal'] { - min-width: 5rem; - width: 100%; - height: 1.25rem; -} - -/* Vertical orientation styles */ -.slider[data-orientation='vertical'] { - height: 5rem; - width: 1.25rem; -} - -.slider-track { - position: relative; - isolation: isolate; - background-color: oklab(1 0 0 / 0.2); - border-radius: inherit; - overflow: hidden; - user-select: none; - outline: 2px solid transparent; - outline-offset: -2px; - transition: outline-offset 150ms ease-out; - box-shadow: 0 0 0 1px oklab(0 0 0 / 0.05); -} - -/* Horizontal track styles */ -.slider-track[data-orientation='horizontal'] { - width: 100%; - height: 0.25rem; -} - -/* Vertical track styles */ -.slider-track[data-orientation='vertical'] { - width: 0.25rem; - height: 100%; -} - -.slider:focus-visible .slider-track { - outline-color: oklch(62.3% 0.214 259.815); - outline-offset: 6px; -} - -.slider-thumb { - width: 0.625rem; - height: 0.625rem; - background-color: oklab(1 0 0); - border-radius: calc(infinity * 1px); - user-select: none; - z-index: 10; - box-shadow: - 0 0 0 1px oklab(0 0 0 / 0.1), - 0 1px 3px 0 oklab(0 0 0 / 0.15), - 0 1px 2px -1px oklab(0 0 0 / 0.15); - opacity: 0; - transition-property: opacity, height, width; - transition-duration: 150ms; - transition-timing-function: ease-out; -} -.slider-thumb:active { - width: 0.75rem; - height: 0.75rem; -} -.slider:hover .slider-thumb, -.slider:focus-within .slider-thumb { - opacity: 1; -} -.slider-track[data-orientation='horizontal'] .slider-thumb { - cursor: ew-resize; -} -.slider-track[data-orientation='vertical'] .slider-thumb { - cursor: ns-resize; -} - -.slider-pointer { - background-color: oklab(1 0 0 / 0.2); - border-radius: inherit; -} - -.slider-progress { - background-color: oklab(1 0 0); - border-radius: inherit; -} - -media-preview-time-display { - font-variant-numeric: tabular-nums; -} - -.popup-animation { - transition-property: transform, scale, opacity, filter; - transition-duration: 200ms; - transform: scale(1); - transform-origin: bottom; - opacity: 1; - filter: blur(0px); -} -.popup-animation[data-starting-style], -.popup-animation[data-ending-style] { - transform: scale(0); - opacity: 0; - filter: blur(8px); -} -.popup-animation[data-instant] { - transition-duration: 0ms; -} - -media-popover { - padding: 0.75rem 0.25rem; - border-radius: calc(infinity * 1px); - margin: 0; - border: 0; -} - -/* Tooltip Component Styles */ -media-tooltip { - margin: 0; - border: none; - box-shadow: none; - background: transparent; - color: oklab(1 0 0); - padding: 0.25rem 0.625rem; - border-radius: calc(infinity * 1px); - font-size: 0.75rem; -} - -.tooltip { - display: none; - white-space: nowrap; -} - -[data-paused] + media-tooltip .play-tooltip, -:not([data-paused]) + media-tooltip .pause-tooltip { - display: block; -} - -[data-fullscreen] + media-tooltip .fullscreen-exit-tooltip, -:not([data-fullscreen]) + media-tooltip .fullscreen-enter-tooltip { - display: block; -} diff --git a/packages/__tech-preview__/examples/html/src/frosted-eject.ts b/packages/__tech-preview__/examples/html/src/frosted-eject.ts deleted file mode 100644 index 68a4b051..00000000 --- a/packages/__tech-preview__/examples/html/src/frosted-eject.ts +++ /dev/null @@ -1,15 +0,0 @@ -import './frosted-eject.css'; -import '@videojs/html-preview/icons'; -// be sure to import video-provider first for proper context initialization -import '@videojs/html-preview/define/video-provider'; -import '@videojs/html-preview/define/media-container'; -import '@videojs/html-preview/define/media-play-button'; -import '@videojs/html-preview/define/media-mute-button'; -import '@videojs/html-preview/define/media-volume-slider'; -import '@videojs/html-preview/define/media-time-slider'; -import '@videojs/html-preview/define/media-fullscreen-button'; -import '@videojs/html-preview/define/media-duration-display'; -import '@videojs/html-preview/define/media-current-time-display'; -import '@videojs/html-preview/define/media-preview-time-display'; -import '@videojs/html-preview/define/media-popover'; -import '@videojs/html-preview/define/media-tooltip'; diff --git a/packages/__tech-preview__/examples/html/src/frosted.ts b/packages/__tech-preview__/examples/html/src/frosted.ts deleted file mode 100644 index 99913f6f..00000000 --- a/packages/__tech-preview__/examples/html/src/frosted.ts +++ /dev/null @@ -1,14 +0,0 @@ -import '@videojs/html-preview/skins/frosted'; - -document.getElementById('app')!.innerHTML = /* html */ ` - - - - - -`; diff --git a/packages/__tech-preview__/examples/html/src/minimal-eject.css b/packages/__tech-preview__/examples/html/src/minimal-eject.css deleted file mode 100644 index b805760b..00000000 --- a/packages/__tech-preview__/examples/html/src/minimal-eject.css +++ /dev/null @@ -1,354 +0,0 @@ -media-container * { - box-sizing: border-box; -} - -media-container { - position: relative; - isolation: isolate; - container: root / inline-size; - overflow: clip; - border-radius: var(--minimal-border-radius, 2rem); - background: oklab(0 0 0); - font-family: ui-sans-serif, system-ui, sans-serif; - font-size: 0.8125rem; - line-height: 1.5; - -webkit-font-smoothing: auto; - -moz-osx-font-smoothing: auto; -} -media-container::after { - content: ''; - position: absolute; - pointer-events: none; - border-radius: inherit; - z-index: 10; - inset: 0; - box-shadow: inset 0 0 0 1px oklab(0 0 0 / 0.15); -} -@media (prefers-color-scheme: dark) { - media-container::after { - box-shadow: inset 0 0 0 1px oklab(1 0 0 / 0.15); - } -} - -/* Fullscreen */ -media-container:fullscreen { - border-radius: 0; -} - -media-container > ::slotted([slot='media']), -media-container > video, -media-container > audio { - display: block; - width: 100%; - height: 100%; -} - -/* Media Container UI Overlay Styling */ -media-container > .overlay { - position: absolute; - inset: 0; - display: flex; - flex-flow: column nowrap; - align-items: start; - pointer-events: none; - border-radius: inherit; - background-image: linear-gradient(to top, oklab(0 0 0 / 0.7), oklab(0 0 0 / 0.5) 7.5rem, rgba(0, 0, 0, 0)); - transition-property: opacity; - transition-duration: 75ms; - transition-delay: 0ms; - transition-timing-function: ease-out; -} -media-container[data-controls='hidden'] > .overlay { - opacity: 0; - transition-duration: 500ms; - transition-delay: 500ms; -} - -/* Media Control Bar UI/Styles */ -.control-bar { - position: absolute; - bottom: 0; - inset-inline: 0; - padding: 2rem 0.375rem 0.375rem 0.375rem; - display: flex; - align-items: center; - gap: 0.5rem; - will-change: transform, filter, opacity; - transition-property: transform, filter, opacity; - transition-delay: 0ms; - transition-duration: 75ms; - transition-timing-function: ease-out; - color: oklab(1 0 0); -} -media-container[data-controls='hidden'] > .control-bar { - opacity: 0; - transform: translateY(100%); - filter: blur(8px); - transition-delay: 500ms; - transition-duration: 500ms; - pointer-events: none; -} -@container root (width > 640px) { - .control-bar { - padding: 2.5rem 0.75rem 0.75rem 0.75rem; - gap: 0.875rem; - } -} - -/* Time Display Styling */ -.time-display-group { - display: flex; - align-items: center; - gap: 0.25rem; -} -.duration-display { - display: contents; - color: oklab(1 0 0 / 0.5); -} -media-current-time-display, -media-duration-display { - text-shadow: 0 1px 0 oklab(0 0 0 / 0.2); - font-variant-numeric: tabular-nums; -} - -.button-group { - display: flex; - align-items: center; - gap: 0.375rem; -} - -/* Generic Media Button Styling */ -.button { - display: grid; - flex-shrink: 0; - padding: 0.625rem; - cursor: pointer; - background: transparent; - border: none; - border-radius: 0.375rem; - color: oklab(1 0 0); - user-select: none; - outline: 2px solid transparent; - outline-offset: -2px; - transition-property: background-color, color, outline-offset; - transition-duration: 150ms; - transition-timing-function: ease-out; -} -.button:hover, -.button:focus-visible, -.button[aria-expanded='true'] { - color: oklab(1 0 0 / 0.8); - text-decoration: none; -} -.button:focus-visible { - outline-color: oklab(1 0 0); - outline-offset: 2px; -} -.button[disabled] { - cursor: not-allowed; - opacity: 0.5; - filter: grayscale(1); -} - -.button .icon { - grid-area: 1 / 1; - width: 18px; - height: 18px; - filter: drop-shadow(0 1px 0 oklab(0 0 0 / 0.4)); -} - -/* Media Play Button UI/Styles */ -media-play-button .icon { - opacity: 0; - transition: opacity 150ms linear; -} - -media-play-button:not([data-paused]) .pause-icon, -media-play-button[data-paused] .play-icon { - opacity: 1; -} - -/* Media Fullscreen Button UI/Styles */ -media-fullscreen-button .icon { - display: none; -} -media-fullscreen-button:not([data-fullscreen]) .fullscreen-enter-icon, -media-fullscreen-button[data-fullscreen] .fullscreen-exit-icon { - display: inline; -} - -/* One way to define the "default visible" icon (CJP) */ -media-mute-button .icon { - display: none; -} -media-mute-button:not([data-volume-level]) .volume-low-icon, -media-mute-button[data-volume-level='high'] .volume-high-icon, -media-mute-button[data-volume-level='medium'] .volume-high-icon, -media-mute-button[data-volume-level='low'] .volume-low-icon, -media-mute-button[data-volume-level='off'] .volume-off-icon { - display: inline; -} - -/* TimeSlider Component Styles */ -.slider { - flex: 1; - display: flex; - align-items: center; - justify-content: center; - position: relative; - border-radius: calc(infinity * 1px); - outline: none; -} - -/* Horizontal orientation styles */ -.slider[data-orientation='horizontal'] { - min-width: 5rem; - width: 100%; - height: 1.25rem; -} - -/* Vertical orientation styles */ -.slider[data-orientation='vertical'] { - height: 4.5rem; - width: 1.25rem; -} - -.slider-track { - position: relative; - isolation: isolate; - background-color: oklab(1 0 0 / 0.2); - border-radius: inherit; - overflow: hidden; - user-select: none; - outline: 2px solid transparent; - outline-offset: -2px; - transition: outline-offset 150ms ease-out; - box-shadow: 0 0 0 1px oklab(0 0 0 / 0.05); -} - -/* Horizontal track styles */ -.slider-track[data-orientation='horizontal'] { - width: 100%; - height: 0.1875rem; -} - -/* Vertical track styles */ -.slider-track[data-orientation='vertical'] { - width: 0.1875rem; - height: 100%; -} - -.slider:focus-visible .slider-track { - outline-color: oklab(1 0 0); - outline-offset: 6px; -} - -.slider-thumb { - width: 0.75rem; - height: 0.75rem; - background-color: oklab(1 0 0); - border-radius: calc(infinity * 1px); - user-select: none; - z-index: 10; - box-shadow: - 0 0 0 1px oklab(0 0 0 / 0.1), - 0 1px 3px 0 oklab(0 0 0 / 0.15), - 0 1px 2px -1px oklab(0 0 0 / 0.15); - opacity: 0; - scale: 0.7; - transform-origin: center; - transition-property: opacity, scale; - transition-duration: 150ms; - transition-timing-function: ease-out; -} -.slider:hover .slider-thumb, -.slider:focus-within .slider-thumb { - opacity: 1; - scale: 1; -} -.slider-track[data-orientation='horizontal'] .slider-thumb { - cursor: ew-resize; -} -.slider-track[data-orientation='vertical'] .slider-thumb { - cursor: ns-resize; -} - -.slider-pointer { - display: none; -} - -.slider-progress { - background-color: oklab(1 0 0); - border-radius: inherit; -} - -media-preview-time-display { - font-variant-numeric: tabular-nums; -} - -.popup-animation { - transition-property: transform, scale, opacity, filter; - transition-duration: 200ms; - transform: scale(1); - transform-origin: bottom; - opacity: 1; - filter: blur(0px); -} -.popup-animation[data-starting-style], -.popup-animation[data-ending-style] { - transform: scale(0); - opacity: 0; - filter: blur(8px); -} -.popup-animation[data-instant] { - transition-duration: 0ms; -} - -media-popover { - padding: 0.5rem; - margin: 0; - border: 0; - background: transparent; -} - -/* Tooltip Component Styles */ -media-tooltip { - margin: 0; - border: none; - box-shadow: none; - background: transparent; - color: oklab(1 0 0); - padding: 0.25rem 0.5rem; - border-radius: 0.25rem; - font-size: 0.75rem; - background-color: oklab(1 0 0 / 0.1); - backdrop-filter: blur(64px) brightness(0.9) saturate(1.5); - box-shadow: - 0 4px 6px -1px oklab(0 0 0 / 0.1), - 0 2px 4px -2px oklab(0 0 0 / 0.1); -} -@media (prefers-reduced-transparency: reduce) { - media-tooltip { - background-color: oklab(0 0 0 / 0.7); - } -} -@media (prefers-contrast: more) { - media-tooltip { - background-color: oklab(0 0 0 / 0.9); - } -} - -.tooltip { - display: none; - white-space: nowrap; -} - -[data-paused] + media-tooltip .play-tooltip, -:not([data-paused]) + media-tooltip .pause-tooltip { - display: block; -} - -[data-fullscreen] + media-tooltip .fullscreen-exit-tooltip, -:not([data-fullscreen]) + media-tooltip .fullscreen-enter-tooltip { - display: block; -} diff --git a/packages/__tech-preview__/examples/html/src/minimal-eject.ts b/packages/__tech-preview__/examples/html/src/minimal-eject.ts deleted file mode 100644 index 78eaca68..00000000 --- a/packages/__tech-preview__/examples/html/src/minimal-eject.ts +++ /dev/null @@ -1,15 +0,0 @@ -import './minimal-eject.css'; -import '@videojs/html-preview/icons'; -// be sure to import video-provider first for proper context initialization -import '@videojs/html-preview/define/video-provider'; -import '@videojs/html-preview/define/media-container'; -import '@videojs/html-preview/define/media-play-button'; -import '@videojs/html-preview/define/media-mute-button'; -import '@videojs/html-preview/define/media-volume-slider'; -import '@videojs/html-preview/define/media-time-slider'; -import '@videojs/html-preview/define/media-fullscreen-button'; -import '@videojs/html-preview/define/media-duration-display'; -import '@videojs/html-preview/define/media-current-time-display'; -import '@videojs/html-preview/define/media-preview-time-display'; -import '@videojs/html-preview/define/media-popover'; -import '@videojs/html-preview/define/media-tooltip'; diff --git a/packages/__tech-preview__/examples/html/src/minimal.ts b/packages/__tech-preview__/examples/html/src/minimal.ts deleted file mode 100644 index 155cf598..00000000 --- a/packages/__tech-preview__/examples/html/src/minimal.ts +++ /dev/null @@ -1,14 +0,0 @@ -import '@videojs/html-preview/skins/minimal'; - -document.getElementById('app')!.innerHTML = /* html */ ` - - - - - -`; diff --git a/packages/__tech-preview__/examples/html/tsconfig.json b/packages/__tech-preview__/examples/html/tsconfig.json deleted file mode 100644 index cba98f94..00000000 --- a/packages/__tech-preview__/examples/html/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "baseUrl": "." - }, - "include": ["src"] -} diff --git a/packages/__tech-preview__/examples/html/vite.config.ts b/packages/__tech-preview__/examples/html/vite.config.ts deleted file mode 100644 index 63cd4cad..00000000 --- a/packages/__tech-preview__/examples/html/vite.config.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { resolve } from 'node:path'; -import { defineConfig } from 'vite'; - -// https://vitejs.dev/config -export default defineConfig({ - server: { - port: 5174, - }, - optimizeDeps: { - exclude: ['@vjs/html'], - }, - build: { - rollupOptions: { - input: { - main: resolve(__dirname, 'index.html'), - minimal: resolve(__dirname, 'minimal.html'), - 'minimal-eject': resolve(__dirname, 'minimal-eject.html'), - frosted: resolve(__dirname, 'frosted.html'), - 'frosted-eject': resolve(__dirname, 'frosted-eject.html'), - }, - }, - }, -}); diff --git a/packages/__tech-preview__/examples/next/README.md b/packages/__tech-preview__/examples/next/README.md deleted file mode 100644 index e215bc4c..00000000 --- a/packages/__tech-preview__/examples/next/README.md +++ /dev/null @@ -1,36 +0,0 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). - -## Getting Started - -First, run the development server: - -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev -``` - -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. - -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. - -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. - -## Learn More - -To learn more about Next.js, take a look at the following resources: - -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! - -## Deploy on Vercel - -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/packages/__tech-preview__/examples/next/app/favicon.ico b/packages/__tech-preview__/examples/next/app/favicon.ico deleted file mode 100644 index a8fcc56f..00000000 Binary files a/packages/__tech-preview__/examples/next/app/favicon.ico and /dev/null differ diff --git a/packages/__tech-preview__/examples/next/app/globals.css b/packages/__tech-preview__/examples/next/app/globals.css deleted file mode 100644 index 37d72f8a..00000000 --- a/packages/__tech-preview__/examples/next/app/globals.css +++ /dev/null @@ -1,26 +0,0 @@ -@import 'tailwindcss'; - -:root { - --background: #ffffff; - --foreground: #171717; -} - -@theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --font-sans: var(--font-geist-sans); - --font-mono: var(--font-geist-mono); -} - -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } -} - -body { - background: var(--background); - color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; -} diff --git a/packages/__tech-preview__/examples/next/app/layout.tsx b/packages/__tech-preview__/examples/next/app/layout.tsx deleted file mode 100644 index 8da647e3..00000000 --- a/packages/__tech-preview__/examples/next/app/layout.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import type { Metadata } from 'next'; -import { Geist, Geist_Mono } from 'next/font/google'; -import './globals.css'; - -const geistSans = Geist({ - variable: '--font-geist-sans', - subsets: ['latin'], -}); - -const geistMono = Geist_Mono({ - variable: '--font-geist-mono', - subsets: ['latin'], -}); - -export const metadata: Metadata = { - title: 'Create Next App', - description: 'Generated by create next app', -}; - -export default function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { - return ( - - - {children} - - - ); -} diff --git a/packages/__tech-preview__/examples/next/app/page.tsx b/packages/__tech-preview__/examples/next/app/page.tsx deleted file mode 100644 index 6bbd2732..00000000 --- a/packages/__tech-preview__/examples/next/app/page.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { FrostedSkin, HlsVideo, MinimalSkin, VideoProvider } from '@videojs/react-preview'; -import '@videojs/react-preview/skins/frosted.css'; -import '@videojs/react-preview/skins/minimal.css'; - -export default function Home() { - return ( -
-

Frosted Skin

- - - {/* @ts-expect-error -- types are incorrect */} - - - -

Minimal Skin

- - - {/* @ts-expect-error -- types are incorrect */} - - - -
- ); -} diff --git a/packages/__tech-preview__/examples/next/eslint.config.mjs b/packages/__tech-preview__/examples/next/eslint.config.mjs deleted file mode 100644 index b22ec515..00000000 --- a/packages/__tech-preview__/examples/next/eslint.config.mjs +++ /dev/null @@ -1,18 +0,0 @@ -import nextVitals from 'eslint-config-next/core-web-vitals'; -import nextTs from 'eslint-config-next/typescript'; -import { defineConfig, globalIgnores } from 'eslint/config'; - -const eslintConfig = defineConfig([ - ...nextVitals, - ...nextTs, - // Override default ignores of eslint-config-next. - globalIgnores([ - // Default ignores of eslint-config-next: - '.next/**', - 'out/**', - 'build/**', - 'next-env.d.ts', - ]), -]); - -export default eslintConfig; diff --git a/packages/__tech-preview__/examples/next/next.config.ts b/packages/__tech-preview__/examples/next/next.config.ts deleted file mode 100644 index 5e891cf0..00000000 --- a/packages/__tech-preview__/examples/next/next.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { NextConfig } from 'next'; - -const nextConfig: NextConfig = { - /* config options here */ -}; - -export default nextConfig; diff --git a/packages/__tech-preview__/examples/next/package.json b/packages/__tech-preview__/examples/next/package.json deleted file mode 100644 index 6a4fbf8b..00000000 --- a/packages/__tech-preview__/examples/next/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "next-demo", - "version": "0.1.0", - "private": true, - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "eslint" - }, - "dependencies": { - "next": "16.0.10", - "react": "19.2.3", - "react-dom": "19.2.3" - }, - "devDependencies": { - "@tailwindcss/postcss": "^4.1.17", - "@types/node": "^20.19.25", - "@types/react": "^19.2.7", - "@types/react-dom": "^19.2.3", - "@videojs/react-preview": "workspace:*", - "eslint": "^9.39.1", - "eslint-config-next": "16.0.8", - "tailwindcss": "^4.1.17", - "typescript": "^5.9.3" - } -} diff --git a/packages/__tech-preview__/examples/next/postcss.config.mjs b/packages/__tech-preview__/examples/next/postcss.config.mjs deleted file mode 100644 index 297374d8..00000000 --- a/packages/__tech-preview__/examples/next/postcss.config.mjs +++ /dev/null @@ -1,7 +0,0 @@ -const config = { - plugins: { - '@tailwindcss/postcss': {}, - }, -}; - -export default config; diff --git a/packages/__tech-preview__/examples/next/public/.gitkeep b/packages/__tech-preview__/examples/next/public/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/packages/__tech-preview__/examples/next/tsconfig.json b/packages/__tech-preview__/examples/next/tsconfig.json deleted file mode 100644 index f94bba48..00000000 --- a/packages/__tech-preview__/examples/next/tsconfig.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "compilerOptions": { - "incremental": true, - "target": "ES2017", - "jsx": "react-jsx", - "lib": ["dom", "dom.iterable", "esnext"], - "module": "esnext", - "moduleResolution": "bundler", - "paths": { - "@/*": ["./*"] - }, - "resolveJsonModule": true, - "allowJs": true, - "strict": true, - "noEmit": true, - "esModuleInterop": true, - "isolatedModules": true, - "skipLibCheck": true, - "plugins": [ - { - "name": "next" - } - ] - }, - "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - ".next/types/**/*.ts", - ".next/dev/types/**/*.ts", - "**/*.mts" - ], - "exclude": ["node_modules"] -} diff --git a/packages/__tech-preview__/examples/react/index.html b/packages/__tech-preview__/examples/react/index.html deleted file mode 100644 index 913d12f8..00000000 --- a/packages/__tech-preview__/examples/react/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - Video.js - React - - - - -
- - - diff --git a/packages/__tech-preview__/examples/react/package.json b/packages/__tech-preview__/examples/react/package.json deleted file mode 100644 index 904f1f37..00000000 --- a/packages/__tech-preview__/examples/react/package.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "react-demo", - "type": "module", - "version": "1.0.0", - "private": true, - "description": "React example for Video.js 10", - "scripts": { - "dev": "vite", - "build": "vite build", - "preview": "vite preview" - }, - "dependencies": { - "@base-ui-components/react": "^1.0.0-rc.0", - "@heroicons/react": "^2.2.0", - "@videojs/react-preview": "workspace:*", - "@videojs/utils-preview": "workspace:*", - "clsx": "^2.1.1", - "motion": "^12.23.25", - "react": "^19.2.1", - "react-dom": "^19.2.1", - "shiki": "^3.19.0", - "tailwindcss": "^4.1.17", - "wouter": "^3.8.1" - }, - "devDependencies": { - "@tailwindcss/vite": "^4.1.17", - "@types/react": "^19.2.7", - "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^5.1.2", - "typescript": "^5.9.3", - "vite": "^7.2.4" - } -} diff --git a/packages/__tech-preview__/examples/react/src/App.tsx b/packages/__tech-preview__/examples/react/src/App.tsx deleted file mode 100644 index 424fa54b..00000000 --- a/packages/__tech-preview__/examples/react/src/App.tsx +++ /dev/null @@ -1,146 +0,0 @@ -import type { PropsWithChildren } from 'react'; -import { VideoProvider } from '@videojs/react-preview'; -import clsx from 'clsx'; -import { useEffect, useMemo, useRef } from 'react'; -import { Link as LinkPrimitive, Redirect, Route, Switch, useLocation } from 'wouter'; -import { VideoElement } from './components'; - -import { SkinLayout } from './components/SkinLayout'; -import { useMediaQuery } from './hooks'; -import CustomBaseUISkin from './skins/custom/base-ui/CustomBaseUISkin'; -import customBaseUISkinSource from './skins/custom/base-ui/CustomBaseUISkin?raw'; -import CustomNativeSkin from './skins/custom/native/CustomNativeSkin'; -import customNativeSkinSource from './skins/custom/native/CustomNativeSkin?raw'; -import FrostedSkinEjected from './skins/ejected/frosted/FrostedSkin'; -import frostedSkinEjectedSource from './skins/ejected/frosted/FrostedSkin?raw'; -import MinimalSkinEjected from './skins/ejected/minimal/MinimalSkin'; -import minimalSkinEjectedSource from './skins/ejected/minimal/MinimalSkin?raw'; -import FrostedSkin from './skins/imported/FrostedSkin'; -import frostedSkinSource from './skins/imported/FrostedSkin?raw'; -import MinimalSkin from './skins/imported/MinimalSkin'; -import minimalSkinSource from './skins/imported/MinimalSkin?raw'; - -import './globals.css'; - -type NavigationSectionProps = PropsWithChildren<{ - title: string; -}>; - -function NavigationSection(props: NavigationSectionProps): JSX.Element { - return ( -
-
- {props.title} -
- -
- ); -} - -type LinkProps = PropsWithChildren<{ - to: string; -}>; - -function Link(props: LinkProps): JSX.Element { - return ( - clsx('flex px-3.5 py-2 rounded-lg transition-colors text-sm', { - 'text-zinc-600 hover:bg-zinc-100 dark:text-zinc-400 dark:hover:bg-zinc-700': !isActive, - 'bg-zinc-200/60 text-zinc-900 dark:bg-zinc-200': isActive, - })} - /> - ); -} - -export default function App(): JSX.Element { - const skinProps = useMemo(() => ({ - className: 'aspect-video shadow-lg shadow-black/15', - children: , - }), []); - - const isDesktop = useMediaQuery('(min-width: 768px)'); - const navigationRef = useRef(null); - - useEffect(() => { - if (isDesktop) { - navigationRef.current?.removeAttribute('popover'); - } else { - navigationRef.current?.setAttribute('popover', 'auto'); - } - }, [isDesktop]); - - const [location] = useLocation(); - useEffect(() => { - try { - navigationRef.current?.hidePopover(); - } catch {} - }, [location]); - - return ( -
- - - - -
- - - - } /> - - - } /> - - - - } /> - - - } /> - - - - } /> - - - } /> - - - - - - - -
-
- ); -} diff --git a/packages/__tech-preview__/examples/react/src/components/CodeBlock.tsx b/packages/__tech-preview__/examples/react/src/components/CodeBlock.tsx deleted file mode 100644 index 74e2e7ce..00000000 --- a/packages/__tech-preview__/examples/react/src/components/CodeBlock.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { useEffect, useState } from 'react'; -import { codeToHtml } from 'shiki'; -import { useColorScheme } from '@/hooks'; - -export interface CodeBlockProps { - code: string; -} - -export function CodeBlock(props: CodeBlockProps): JSX.Element { - const { code } = props; - const [output, setOutput] = useState(''); - const colorScheme = useColorScheme(); - - useEffect(() => { - codeToHtml(code, { - theme: colorScheme === 'light' ? 'vitesse-light' : 'vitesse-dark', - lang: 'javascript', - }).then((result) => { - setOutput(result); - }); - }, [code, colorScheme]); - - return ( -
- ); -} diff --git a/packages/__tech-preview__/examples/react/src/components/SkinLayout.tsx b/packages/__tech-preview__/examples/react/src/components/SkinLayout.tsx deleted file mode 100644 index 65c5f1b8..00000000 --- a/packages/__tech-preview__/examples/react/src/components/SkinLayout.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import type { ReactNode } from 'react'; -import { CodeBlock } from './CodeBlock'; -import { Tabs } from './Tabs'; - -export interface SkinLayoutProps { - code: string; - preview: ReactNode; -} - -export function SkinLayout(props: SkinLayoutProps): JSX.Element { - const { code, preview } = props; - - return ( - - - Preview - Source - - - - - {preview} - - - - - - ); -} diff --git a/packages/__tech-preview__/examples/react/src/components/Tabs/Tab.tsx b/packages/__tech-preview__/examples/react/src/components/Tabs/Tab.tsx deleted file mode 100644 index 5543bdca..00000000 --- a/packages/__tech-preview__/examples/react/src/components/Tabs/Tab.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import type { ComponentProps } from 'react'; -import { Tabs as TabsPrimitive } from '@base-ui-components/react/tabs'; -import clsx from 'clsx'; - -export function Tab(props: ComponentProps): JSX.Element { - const { className, ...rest } = props; - return ( - - ); -} diff --git a/packages/__tech-preview__/examples/react/src/components/Tabs/TabIndicator.tsx b/packages/__tech-preview__/examples/react/src/components/Tabs/TabIndicator.tsx deleted file mode 100644 index e6c4a640..00000000 --- a/packages/__tech-preview__/examples/react/src/components/Tabs/TabIndicator.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import type { ComponentProps } from 'react'; -import { Tabs as TabsPrimitive } from '@base-ui-components/react/tabs'; -import clsx from 'clsx'; - -export function TabIndicator(props: ComponentProps): JSX.Element { - const { className, ...rest } = props; - return ( - - ); -} diff --git a/packages/__tech-preview__/examples/react/src/components/Tabs/TabList.tsx b/packages/__tech-preview__/examples/react/src/components/Tabs/TabList.tsx deleted file mode 100644 index 53ee06fa..00000000 --- a/packages/__tech-preview__/examples/react/src/components/Tabs/TabList.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import type { ComponentProps } from 'react'; -import { Tabs as TabsPrimitive } from '@base-ui-components/react/tabs'; -import clsx from 'clsx'; - -export function TabList(props: ComponentProps): JSX.Element { - const { className, ...rest } = props; - return ( - - ); -} diff --git a/packages/__tech-preview__/examples/react/src/components/Tabs/TabPanel.tsx b/packages/__tech-preview__/examples/react/src/components/Tabs/TabPanel.tsx deleted file mode 100644 index 795969b8..00000000 --- a/packages/__tech-preview__/examples/react/src/components/Tabs/TabPanel.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import type { ComponentProps } from 'react'; -import { Tabs as TabsPrimitive } from '@base-ui-components/react/tabs'; -import clsx from 'clsx'; - -export function TabPanel(props: ComponentProps): JSX.Element { - const { className, ...rest } = props; - return ( - - ); -} diff --git a/packages/__tech-preview__/examples/react/src/components/Tabs/index.tsx b/packages/__tech-preview__/examples/react/src/components/Tabs/index.tsx deleted file mode 100644 index c8eeb306..00000000 --- a/packages/__tech-preview__/examples/react/src/components/Tabs/index.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { Tabs as TabsPrimitive } from '@base-ui-components/react/tabs'; - -import { Tab } from './Tab'; -import { TabIndicator } from './TabIndicator'; -import { TabList } from './TabList'; -import { TabPanel } from './TabPanel'; - -export const Tabs = { - ...TabsPrimitive, - Tab, - List: TabList, - Panel: TabPanel, - Indicator: TabIndicator, -} as typeof TabsPrimitive; diff --git a/packages/__tech-preview__/examples/react/src/components/VideoElement.tsx b/packages/__tech-preview__/examples/react/src/components/VideoElement.tsx deleted file mode 100644 index b26623a4..00000000 --- a/packages/__tech-preview__/examples/react/src/components/VideoElement.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { HlsVideo } from '@videojs/react-preview'; - -export function VideoElement(): JSX.Element { - return ( - - ); -} diff --git a/packages/__tech-preview__/examples/react/src/components/index.ts b/packages/__tech-preview__/examples/react/src/components/index.ts deleted file mode 100644 index 61d57c01..00000000 --- a/packages/__tech-preview__/examples/react/src/components/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './CodeBlock'; -export * from './SkinLayout'; -export * from './Tabs'; -export * from './VideoElement'; diff --git a/packages/__tech-preview__/examples/react/src/globals.css b/packages/__tech-preview__/examples/react/src/globals.css deleted file mode 100644 index 9f038740..00000000 --- a/packages/__tech-preview__/examples/react/src/globals.css +++ /dev/null @@ -1,45 +0,0 @@ -@import 'tailwindcss'; - -@theme { - --font-sans: - InterVariable, ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', - 'Noto Color Emoji'; -} - -/* Make reduced-transparency variant */ -@custom-variant reduced-transparency @media (prefers-reduced-transparency: reduce); - -.vjs { - font-family: var(--font-sans); -} - -[popover].popover-from-left { - translate: -100% 0; -} - -[popover] { - transition: - opacity 0.25s, - translate 0.25s, - overlay 0.25s allow-discrete, - display 0.25s allow-discrete; - opacity: 0; - - &:popover-open { - opacity: 1; - translate: 0 0; - } -} - -@starting-style { - [popover] { - &:popover-open { - opacity: 0; - } - } - [popover].popover-from-left { - &:popover-open { - translate: -100% 0; - } - } -} diff --git a/packages/__tech-preview__/examples/react/src/hooks/index.ts b/packages/__tech-preview__/examples/react/src/hooks/index.ts deleted file mode 100644 index 47757a94..00000000 --- a/packages/__tech-preview__/examples/react/src/hooks/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './useColorScheme'; -export * from './useMediaQuery'; diff --git a/packages/__tech-preview__/examples/react/src/hooks/useColorScheme.ts b/packages/__tech-preview__/examples/react/src/hooks/useColorScheme.ts deleted file mode 100644 index 975047f2..00000000 --- a/packages/__tech-preview__/examples/react/src/hooks/useColorScheme.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { useEffect, useState } from 'react'; - -type ColorScheme = 'light' | 'dark'; - -export function useColorScheme(): ColorScheme { - const [colorScheme, setColorScheme] = useState(() => { - if (typeof window === 'undefined') { - return 'light'; - } - return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; - }); - - useEffect(() => { - const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); - - const handleChange = (event: MediaQueryListEvent) => { - setColorScheme(event.matches ? 'dark' : 'light'); - }; - - mediaQuery.addEventListener('change', handleChange); - - return () => { - mediaQuery.removeEventListener('change', handleChange); - }; - }, []); - - return colorScheme; -} diff --git a/packages/__tech-preview__/examples/react/src/hooks/useIsomorphicLayoutEffect.ts b/packages/__tech-preview__/examples/react/src/hooks/useIsomorphicLayoutEffect.ts deleted file mode 100644 index 008f79ec..00000000 --- a/packages/__tech-preview__/examples/react/src/hooks/useIsomorphicLayoutEffect.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { useEffect, useLayoutEffect } from 'react'; - -export const useIsomorphicLayoutEffect: typeof useLayoutEffect | typeof useEffect - = typeof window !== 'undefined' ? useLayoutEffect : useEffect; diff --git a/packages/__tech-preview__/examples/react/src/hooks/useMediaQuery.ts b/packages/__tech-preview__/examples/react/src/hooks/useMediaQuery.ts deleted file mode 100644 index b9bd7084..00000000 --- a/packages/__tech-preview__/examples/react/src/hooks/useMediaQuery.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { useState } from 'react'; - -import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'; - -interface UseMediaQueryOptions { - defaultValue?: boolean; - initializeWithValue?: boolean; -} - -const IS_SERVER = typeof window === 'undefined'; - -export function useMediaQuery( - query: string, - { - defaultValue = false, - initializeWithValue = true, - }: UseMediaQueryOptions = {}, -): boolean { - const getMatches = (query: string): boolean => { - if (IS_SERVER) { - return defaultValue; - } - return window.matchMedia(query).matches; - }; - - const [matches, setMatches] = useState(() => { - if (initializeWithValue) { - return getMatches(query); - } - return defaultValue; - }); - - // Handles the change event of the media query. - function handleChange() { - setMatches(getMatches(query)); - } - - useIsomorphicLayoutEffect(() => { - const matchMedia = window.matchMedia(query); - - // Triggered at the first client-side load and if query changes - handleChange(); - - matchMedia.addEventListener('change', handleChange); - - return () => { - matchMedia.removeEventListener('change', handleChange); - }; - }, [query]); - - return matches; -} diff --git a/packages/__tech-preview__/examples/react/src/main.tsx b/packages/__tech-preview__/examples/react/src/main.tsx deleted file mode 100644 index 9fa2516d..00000000 --- a/packages/__tech-preview__/examples/react/src/main.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { StrictMode } from 'react'; -import { createRoot } from 'react-dom/client'; - -import App from './App'; - -createRoot(document.getElementById('root')!).render( - - - , -); diff --git a/packages/__tech-preview__/examples/react/src/skins/custom/base-ui/CustomBaseUISkin.tsx b/packages/__tech-preview__/examples/react/src/skins/custom/base-ui/CustomBaseUISkin.tsx deleted file mode 100644 index 54ea9add..00000000 --- a/packages/__tech-preview__/examples/react/src/skins/custom/base-ui/CustomBaseUISkin.tsx +++ /dev/null @@ -1,366 +0,0 @@ -import type { ButtonProps as ButtonPrimitiveProps } from '@base-ui-components/react/button'; -import type { SliderRootProps } from '@base-ui-components/react/slider'; -import type { ComponentProps, PropsWithChildren } from 'react'; -import { Button as ButtonPrimitive } from '@base-ui-components/react/button'; -import { Popover as PopoverPrimitive } from '@base-ui-components/react/popover'; -import { Slider as SliderPrimitive } from '@base-ui-components/react/slider'; -import { Tooltip as TooltipPrimitive } from '@base-ui-components/react/tooltip'; - -import { - ArrowDownLeftIcon, - ArrowUpRightIcon, -} from '@heroicons/react/16/solid'; -import { - PauseIcon, - PlayIcon, - SpeakerWaveIcon, - SpeakerXMarkIcon, - XMarkIcon, -} from '@heroicons/react/20/solid'; -import { - MediaContainer, - useCurrentTimeDisplayState, - useFullscreenButtonState, - useMuteButtonState, - usePlayButtonState, - useTimeSliderRootState, - useVolumeSliderRootState, -} from '@videojs/react-preview'; -import { formatDisplayTime } from '@videojs/utils-preview'; - -import clsx from 'clsx'; -import { useCallback, useState } from 'react'; - -type SkinProps = PropsWithChildren<{ - className?: string; -}>; - -function Surface(props: ComponentProps<'div'>) { - const { className, ...rest } = props; - return ( -
- ); -} - -function Popup(props: ComponentProps) { - const { className, ...rest } = props; - return ( - - ); -} - -function TooltipPopup(props: ComponentProps) { - const { className, ...rest } = props; - return ( - } - {...rest} - /> - ); -} - -const Tooltip = { - ...TooltipPrimitive, - Popup: TooltipPopup, -}; - -function PopoverPopup(props: ComponentProps) { - const { className, ...rest } = props; - return ( - } - {...rest} - /> - ); -} - -const Popover = { - ...PopoverPrimitive, - Popup: PopoverPopup, -}; - -type ButtonProps = Omit; - -function Button(props: ButtonProps) { - return ( - - ); -} - -function PlayButton() { - const { paused, requestPause, requestPlay } = usePlayButtonState(); - - const onClick = useCallback(() => { - if (paused) { - requestPlay(); - } else { - requestPause(); - } - }, [paused, requestPlay, requestPause]); - - const label = paused ? 'Play' : 'Pause'; - - return ( - - - {paused ? : } - {label} - - )} - /> - - - - {label} - - - - - ); -} - -function MuteButton() { - const { muted, requestMute, requestUnmute } = useMuteButtonState(); - const [open, setOpen] = useState(false); - - const onClick = useCallback(() => { - if (muted) { - requestUnmute(); - } else { - requestMute(); - } - }, [muted, requestMute, requestUnmute]); - - const handleOpenChange = useCallback((isOpen: boolean, eventDetails: PopoverPrimitive.Root.ChangeEventDetails) => { - // Don't close when clicking the trigger button - only allow hover to control it - if (!isOpen && eventDetails?.reason === 'trigger-press') return; - setOpen(isOpen); - }, []); - - const label = muted ? 'Unmute' : 'Mute'; - - return ( - - - {muted ? : } - {label} - - )} - /> - - - - - - - - - - - ); -} - -function FullscreenButton() { - const { fullscreen, requestEnterFullscreen, requestExitFullscreen } = useFullscreenButtonState(); - - const onClick = useCallback(() => { - if (fullscreen) { - requestExitFullscreen(); - } else { - requestEnterFullscreen(); - } - }, [fullscreen, requestEnterFullscreen, requestExitFullscreen]); - - const label = fullscreen ? 'Exit fullscreen' : 'Enter fullscreen'; - - return ( - - - {fullscreen - ? - : ( -
- - -
- )} - {label} - - )} - /> - - - - {label} - - - -
- ); -} - -type SliderProps = Pick, 'min' | 'max' | 'step' | 'value' | 'onValueChange' | 'orientation'>; - -function Slider(props: SliderProps) { - return ( - - - - - - - - ); -} - -function TimeSlider() { - // TODO: Expose something like _fillWidth to facilitate debouncing etc. - const { currentTime, duration, requestSeek } = useTimeSliderRootState(); - - return ( - - ); -} - -function VolumeSlider() { - const { volume, muted, requestVolumeChange } = useVolumeSliderRootState(); - // FIXME: If seems that volume can be undefined initially, despite the types. - const value = muted ? 0 : volume ?? 0; - - return ( - - ); -} - -interface TimeDisplayProps { - type: 'current' | 'duration'; - className?: string; -} - -function TimeDisplay(props: TimeDisplayProps) { - const { type, className } = props; - const { duration, currentTime } = useCurrentTimeDisplayState(); - const value = formatDisplayTime(type === 'current' ? currentTime : duration); - - return {value}; -} - -export default function CustomBaseUISkin({ children, className }: SkinProps): JSX.Element { - return ( - - {/* FIXME: Mismatch between React 18 & 19 types */} - {children as any} - - - - - -
-
- - / - -
- - -
- - - - -
-
-
- ); -}; diff --git a/packages/__tech-preview__/examples/react/src/skins/custom/native/CustomNativeSkin.tsx b/packages/__tech-preview__/examples/react/src/skins/custom/native/CustomNativeSkin.tsx deleted file mode 100644 index 95deea1f..00000000 --- a/packages/__tech-preview__/examples/react/src/skins/custom/native/CustomNativeSkin.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import type { PropsWithChildren } from 'react'; - -import { - CurrentTimeDisplay, - DurationDisplay, - FullscreenButton, - MediaContainer, - MuteButton, - PlayButton, - TimeSlider, - VolumeSlider, -} from '@videojs/react-preview'; -import { - FullscreenEnterIcon, - FullscreenExitIcon, - PauseIcon, - PlayIcon, - VolumeHighIcon, - VolumeLowIcon, - VolumeOffIcon, -} from '@videojs/react-preview/icons'; -import styles from './styles'; - -type SkinProps = PropsWithChildren<{ - className?: string; -}>; - -export default function CustomNativeSkin({ children, className = '' }: SkinProps): JSX.Element { - return ( - - {/* FIXME: Mismatch between React 18 & 19 types */} - {children as any} - -
-
- - - - - - - -
- -
-
- - - - - -
- - / - -
-
- -
-
- - - - - - -
- - - - - - -
-
- - - - - -
-
-
-
- ); -}; diff --git a/packages/__tech-preview__/examples/react/src/skins/custom/native/styles.ts b/packages/__tech-preview__/examples/react/src/skins/custom/native/styles.ts deleted file mode 100644 index 4df6740a..00000000 --- a/packages/__tech-preview__/examples/react/src/skins/custom/native/styles.ts +++ /dev/null @@ -1,122 +0,0 @@ -import type { CustomNativeSkinStyles } from './types'; - -// NOTE: Removing import to sidestep for compiler complexity (CJP) -// import { cn } from '../../utils/cn'; -// A (very crude) utility to merge class names -// Usually I'd use something like `clsx` or `classnames` but this is ok for our simple use case. -// It just makes the billions of Tailwind classes a little easier to read. -function cn(...classes: (string | undefined)[]): string { - return classes.filter(Boolean).join(' '); -} - -const styles: CustomNativeSkinStyles = { - MediaContainer: cn( - 'relative isolate @container/root group/root overflow-clip bg-black rounded-xl', - // Base typography - 'font-sans text-[0.8125rem] subpixel-antialiased', - // Fancy borders - 'after:absolute after:inset-0 after:ring-black/10 after:ring-1 dark:after:ring-white/10 after:ring-inset after:z-10 after:pointer-events-none after:rounded-[inherit]', - // Prevent rounded corners in fullscreen - '[&:fullscreen]:rounded-none', - // Ensure the nested video inherits the radius - '[&_video]:w-full [&_video]:h-full', - ), - Controls: cn( - '@container/controls absolute inset-x-0 bottom-0 top-1/3 flex flex-col justify-end z-20 px-2.5 pb-2.5 text-white text-shadow', - 'shadow-sm shadow-black/15', - // Background - 'bg-linear-to-t from-stone-950/70 via-stone-950/60 via-35% to-transparent', - // Animation - 'transition ease-in-out', - // FIXME: Temporary hide/show logic - 'translate-y-full opacity-0 delay-500 pointer-events-none', - 'has-data-paused:translate-y-0 has-data-paused:opacity-100 has-data-paused:delay-0 has-data-paused:pointer-events-auto', - 'group-hover/root:translate-y-0 group-hover/root:opacity-100 group-hover/root:delay-0 group-hover/root:pointer-events-auto', - ), - ControlsRow: cn('flex items-center justify-between'), - Button: cn( - 'group/button cursor-pointer relative shrink-0 transition select-none p-2 rounded-md', - // Background/foreground - 'bg-transparent text-white/90', - // Hover and focus states - 'hover:no-underline hover:bg-stone-100/10 hover:backdrop-blur-md hover:text-white focus-visible:no-underline focus-visible:bg-stone-100/10 focus-visible:text-white', - // Focus state - '-outline-offset-2 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-amber-500', - // Disabled state - 'aria-disabled:grayscale aria-disabled:opacity-50 aria-disabled:cursor-not-allowed', - // Loading state - 'aria-busy:pointer-events-none aria-busy:cursor-not-allowed', - // Expanded state - 'aria-expanded:bg-stone-100/10 aria-expanded:text-white', - // Pressed state - 'active:scale-95', - ), - IconButton: cn( - 'grid [&_svg]:[grid-area:1/1]', - '[&_svg]:shrink-0 [&_svg]:transition [&_svg]:duration-300 [&_svg]:ease-out [&_svg]:drop-shadow-[0_1px_0_var(--tw-shadow-color)] [&_svg]:shadow-black/20', - ), - PlayButton: cn( - '[&_.pause-icon]:opacity-100 [&[data-paused]_.pause-icon]:opacity-0', - '[&_.play-icon]:opacity-0 [&[data-paused]_.play-icon]:opacity-100', - ), - PlayIcon: cn('play-icon'), - PauseIcon: cn('pause-icon'), - VolumeControls: cn('flex items-center flex-row-reverse group/volume'), - VolumeSlider: cn( - 'w-0 px-3 overflow-hidden pointer-events-none transition-[opacity,width] opacity-0 ease-out delay-500', - 'group-hover/volume:w-28 group-hover/volume:pointer-events-auto group-hover/volume:opacity-100 group-hover/volume:delay-0', - 'group-focus-within/volume:w-28 group-focus-within/volume:pointer-events-auto group-focus-within/volume:opacity-100 group-focus-within/volume:delay-0', - ), - VolumeButton: cn( - '[&_svg]:hidden', - '[&[data-volume-level="high"]_.volume-high-icon]:inline', - '[&[data-volume-level="medium"]_.volume-low-icon]:inline', - '[&[data-volume-level="low"]_.volume-low-icon]:inline', - '[&[data-volume-level="off"]_.volume-off-icon]:inline', - ), - VolumeHighIcon: cn('volume-high-icon'), - VolumeLowIcon: cn('volume-low-icon'), - VolumeOffIcon: cn('volume-off-icon'), - FullScreenButton: cn( - '[&_.fullscreen-enter-icon]:opacity-100 [&[data-fullscreen]_.fullscreen-enter-icon]:opacity-0', - '[&_.fullscreen-exit-icon]:opacity-0 [&[data-fullscreen]_.fullscreen-exit-icon]:opacity-100', - '[&_path]:transition-transform ease-out', - ), - FullScreenEnterIcon: cn( - 'fullscreen-enter-icon', - 'group-hover/button:[&_.arrow-1]:-translate-x-px group-hover/button:[&_.arrow-1]:-translate-y-px', - 'group-hover/button:[&_.arrow-2]:translate-x-px group-hover/button:[&_.arrow-2]:translate-y-px', - ), - FullScreenExitIcon: cn( - 'fullscreen-exit-icon', - '[&_.arrow-1]:-translate-x-px [&_.arrow-1]:-translate-y-px', - '[&_.arrow-2]:translate-x-px [&_.arrow-2]:translate-y-px', - 'group-hover/button:[&_.arrow-1]:translate-0', - 'group-hover/button:[&_.arrow-2]:translate-0', - ), - TimeSliderThumb: cn( - 'opacity-0', - 'group-hover/slider:opacity-100 group-focus-within/slider:opacity-100', - ), - TimeDisplay: cn('tabular-nums text-shadow-2xs shadow-black/50'), - SliderRoot: cn( - 'flex items-center justify-center flex-1 group/slider relative', - '[&[data-orientation="horizontal"]]:h-5 [&[data-orientation="horizontal"]]:min-w-20', - '[&[data-orientation="vertical"]]:w-5 [&[data-orientation="vertical"]]:h-20', - ), - SliderTrack: cn( - 'relative select-none rounded-full bg-white/25 backdrop-blur-sm backdrop-brightness-90 backdrop-saturate-150 shadow-sm shadow-black/10', - '[&[data-orientation="horizontal"]]:w-full [&[data-orientation="horizontal"]]:h-1', - '[&[data-orientation="vertical"]]:w-1', - ), - SliderProgress: cn('bg-amber-500 rounded-[inherit]'), - // TODO: Work out what we want to do here. - SliderPointer: cn('rounded-[inherit]'), - SliderThumb: cn( - 'bg-white z-10 select-none ring ring-black/10 rounded-full shadow-sm shadow-black/15 transition-[opacity,height,width] ease-in-out', - '-outline-offset-2 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-amber-500', - 'size-3 active:size-3.5 group-active/slider:size-3.5 hover:cursor-ew-resize', - ), -}; - -export default styles; diff --git a/packages/__tech-preview__/examples/react/src/skins/custom/native/types.ts b/packages/__tech-preview__/examples/react/src/skins/custom/native/types.ts deleted file mode 100644 index 9df84e6b..00000000 --- a/packages/__tech-preview__/examples/react/src/skins/custom/native/types.ts +++ /dev/null @@ -1,26 +0,0 @@ -export interface CustomNativeSkinStyles { - readonly MediaContainer: string; - readonly Controls: string; - readonly ControlsRow: string; - readonly Button: string; - readonly IconButton: string; - readonly PlayButton: string; - readonly PlayIcon: string; - readonly PauseIcon: string; - readonly VolumeControls: string; - readonly VolumeSlider: string; - readonly VolumeButton: string; - readonly VolumeHighIcon: string; - readonly VolumeLowIcon: string; - readonly VolumeOffIcon: string; - readonly FullScreenButton: string; - readonly FullScreenEnterIcon: string; - readonly FullScreenExitIcon: string; - readonly TimeSliderThumb: string; - readonly TimeDisplay: string; - readonly SliderRoot: string; - readonly SliderTrack: string; - readonly SliderProgress: string; - readonly SliderPointer: string; - readonly SliderThumb: string; -} diff --git a/packages/__tech-preview__/examples/react/src/skins/ejected/frosted/FrostedSkin.tsx b/packages/__tech-preview__/examples/react/src/skins/ejected/frosted/FrostedSkin.tsx deleted file mode 100644 index f32bb894..00000000 --- a/packages/__tech-preview__/examples/react/src/skins/ejected/frosted/FrostedSkin.tsx +++ /dev/null @@ -1,120 +0,0 @@ -import type { PropsWithChildren } from 'react'; - -import { - CurrentTimeDisplay, - DurationDisplay, - FullscreenButton, - MediaContainer, - MuteButton, - PlayButton, - Popover, - PreviewTimeDisplay, - TimeSlider, - Tooltip, - VolumeSlider, -} from '@videojs/react-preview'; -import { - FullscreenEnterIcon, - FullscreenExitIcon, - PauseIcon, - PlayIcon, - VolumeHighIcon, - VolumeLowIcon, - VolumeOffIcon, -} from '@videojs/react-preview/icons'; - -import './frosted.css'; - -type SkinProps = PropsWithChildren<{ - className?: string; -}>; - -export default function FrostedSkin({ children, className = '' }: SkinProps): JSX.Element { - return ( - - {/* FIXME: Mismatch between React 18 & 19 types */} - {children as any} - -
- -
- - - - - - - - - - Play - Pause - - - - -
- - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Enter Fullscreen - Exit Fullscreen - - - -
- - ); -} diff --git a/packages/__tech-preview__/examples/react/src/skins/ejected/frosted/frosted.css b/packages/__tech-preview__/examples/react/src/skins/ejected/frosted/frosted.css deleted file mode 100644 index 0a133178..00000000 --- a/packages/__tech-preview__/examples/react/src/skins/ejected/frosted/frosted.css +++ /dev/null @@ -1,352 +0,0 @@ -.vjs-frosted-skin * { - box-sizing: border-box; -} - -.vjs-frosted-skin { - position: relative; - isolation: isolate; - container: root / inline-size; - overflow: clip; - border-radius: var(--frosted-border-radius, 2rem); - background: oklab(0 0 0); - font-family: ui-sans-serif, system-ui, sans-serif; - font-size: 0.8125rem; - line-height: 1.5; - -webkit-font-smoothing: auto; - -moz-osx-font-smoothing: auto; -} -.vjs-frosted-skin::before, -.vjs-frosted-skin::after { - content: ''; - position: absolute; - pointer-events: none; - border-radius: inherit; - z-index: 10; -} -.vjs-frosted-skin::before { - inset: 1px; - box-shadow: inset 0 0 0 1px oklab(1 0 0 / 0.15); -} -.vjs-frosted-skin::after { - inset: 0; - box-shadow: inset 0 0 0 1px oklab(0 0 0 / 0.1); -} - -/* Fullscreen */ -.vjs-frosted-skin:fullscreen { - border-radius: 0; -} - -.vjs-frosted-skin > ::slotted([slot='media']) { - display: block; - width: 100%; - height: 100%; -} - -/* Media Container UI Overlay Styling */ -.vjs-frosted-skin > .overlay { - position: absolute; - inset: 0; - display: flex; - flex-flow: column nowrap; - align-items: start; - pointer-events: none; - border-radius: inherit; - background-image: linear-gradient(to top, oklab(0 0 0 / 0.5), oklab(0 0 0 / 0.3), rgba(0, 0, 0, 0)); - backdrop-filter: saturate(1.5) brightness(0.9); - transition: opacity 0.15s ease-out; - transition-duration: 100ms; - transition-delay: 0ms; -} -.vjs-frosted-skin[data-controls='hidden'] > .overlay { - transition-delay: 500ms; - transition-duration: 300ms; - opacity: 0; -} - -/* Common Surface Styles - e.g. tooltips, popovers, controls */ -.vjs-frosted-skin .surface { - background-color: oklab(1 0 0 / 0.1); - backdrop-filter: blur(64px) brightness(0.9) saturate(1.5); - box-shadow: - inset 0 0 0 1px oklab(1 0 0 / 0.05), - 0 0 0 1px oklab(0 0 0 / 0.15), - oklab(0 0 0 / 0.15) 0px 1px 3px 0px, - oklab(0 0 0 / 0.15) 0px 1px 2px -1px; -} -@media (prefers-reduced-transparency: reduce) { - .vjs-frosted-skin .surface { - background-color: oklab(0 0 0 / 0.7); - box-shadow: - inset 0 0 0 1px oklab(0 0 0), - 0 0 0 1px oklab(1 0 0 / 0.2); - } -} -@media (prefers-contrast: more) { - .vjs-frosted-skin .surface { - background-color: oklab(0 0 0 / 0.9); - box-shadow: - inset 0 0 0 1px oklab(0 0 0), - 0 0 0 1px oklab(1 0 0 / 0.2); - } -} - -/* Media Control Bar UI/Styles */ -.vjs-frosted-skin .control-bar { - position: absolute; - bottom: 0.75rem; - inset-inline: 0.75rem; - padding: 0.25rem; - display: flex; - align-items: center; - gap: 0.125rem; - color: oklab(1 0 0); - border-radius: calc(infinity * 1px); - will-change: scale, transform, filter, opacity; - transition-property: scale, transform, filter, opacity; - transition-timing-function: ease-out; - transition-delay: 0ms; - transition-duration: 100ms; - transform-origin: bottom; -} -.vjs-frosted-skin[data-controls='hidden'] .control-bar { - pointer-events: none; - filter: blur(8px); - scale: 0.9; - opacity: 0; - transition-delay: 500ms; - transition-duration: 300ms; -} - -/* Time Display Styling */ -.vjs-frosted-skin .time-controls { - display: flex; - align-items: center; - flex: 1; - gap: 0.75rem; - padding-inline: 0.375rem; -} -.vjs-frosted-skin .time-display { - text-shadow: 0 1px 0 oklab(0 0 0 / 0.25); - font-variant-numeric: tabular-nums; -} - -/* Generic Media Button Styling */ -.vjs-frosted-skin .button { - display: grid; - flex-shrink: 0; - padding: 0.5rem; - cursor: pointer; - background: transparent; - border: none; - border-radius: calc(infinity * 1px); - color: oklab(1 0 0 / 0.9); - user-select: none; - outline: 2px solid transparent; - outline-offset: -2px; - transition-property: background-color, color, outline-offset; - transition-duration: 150ms; - transition-timing-function: ease-out; -} -.vjs-frosted-skin .button:hover, -.vjs-frosted-skin .button:focus-visible, -.vjs-frosted-skin .button[aria-expanded='true'] { - background-color: oklab(1 0 0 / 0.1); - color: oklab(1 0 0); - text-decoration: none; -} -.vjs-frosted-skin .button:focus-visible { - outline-color: oklch(62.3% 0.214 259.815); - outline-offset: 2px; -} -.vjs-frosted-skin .button[disabled] { - cursor: not-allowed; - opacity: 0.5; - filter: grayscale(1); -} - -.vjs-frosted-skin .button .icon { - grid-area: 1 / 1; - width: 18px; - height: 18px; - filter: drop-shadow(0 1px 0 oklab(0 0 0 / 0.25)); -} - -/* Media Play Button UI/Styles */ -.vjs-frosted-skin .play-button .icon { - opacity: 0; - transition: opacity 150ms linear; -} - -.vjs-frosted-skin .play-button:not([data-paused]) .pause-icon, -.vjs-frosted-skin .play-button[data-paused] .play-icon { - opacity: 1; -} - -/* Media Fullscreen Button UI/Styles */ -.vjs-frosted-skin .fullscreen-button .icon { - display: none; -} -.vjs-frosted-skin .fullscreen-button:not([data-fullscreen]) .fullscreen-enter-icon, -.vjs-frosted-skin .fullscreen-button[data-fullscreen] .fullscreen-exit-icon { - display: inline; -} - -/* One way to define the "default visible" icon (CJP) */ -.vjs-frosted-skin .mute-button .icon { - display: none; -} -.vjs-frosted-skin .mute-button:not([data-volume-level]) .volume-low-icon, -.vjs-frosted-skin .mute-button[data-volume-level='high'] .volume-high-icon, -.vjs-frosted-skin .mute-button[data-volume-level='medium'] .volume-high-icon, -.vjs-frosted-skin .mute-button[data-volume-level='low'] .volume-low-icon, -.vjs-frosted-skin .mute-button[data-volume-level='off'] .volume-off-icon { - display: inline; -} - -/* TimeSlider Component Styles */ -.vjs-frosted-skin .slider { - flex: 1; - display: flex; - align-items: center; - justify-content: center; - position: relative; - border-radius: calc(infinity * 1px); - outline: none; -} - -/* Horizontal orientation styles */ -.vjs-frosted-skin .slider[data-orientation='horizontal'] { - min-width: 5rem; - width: 100%; - height: 1.25rem; -} - -/* Vertical orientation styles */ -.vjs-frosted-skin .slider[data-orientation='vertical'] { - height: 5rem; - width: 1.25rem; -} - -.vjs-frosted-skin .slider-track { - position: relative; - isolation: isolate; - background-color: oklab(1 0 0 / 0.2); - border-radius: inherit; - overflow: hidden; - user-select: none; - outline: 2px solid transparent; - outline-offset: -2px; - transition: outline-offset 150ms ease-out; - box-shadow: 0 0 0 1px oklab(0 0 0 / 0.05); -} - -/* Horizontal track styles */ -.vjs-frosted-skin .slider-track[data-orientation='horizontal'] { - width: 100%; - height: 0.25rem; -} - -/* Vertical track styles */ -.vjs-frosted-skin .slider-track[data-orientation='vertical'] { - width: 0.25rem; - height: 100%; -} - -.vjs-frosted-skin .slider:focus-visible .slider-track { - outline-color: oklch(62.3% 0.214 259.815); - outline-offset: 6px; -} - -.vjs-frosted-skin .slider-thumb { - width: 0.625rem; - height: 0.625rem; - background-color: oklab(1 0 0); - border-radius: calc(infinity * 1px); - user-select: none; - z-index: 10; - box-shadow: - 0 0 0 1px oklab(0 0 0 / 0.1), - 0 1px 3px 0 oklab(0 0 0 / 0.15), - 0 1px 2px -1px oklab(0 0 0 / 0.15); - opacity: 0; - transition-property: opacity, height, width; - transition-duration: 150ms; - transition-timing-function: ease-out; -} -.vjs-frosted-skin .slider-thumb:active { - width: 0.75rem; - height: 0.75rem; -} -.vjs-frosted-skin .slider:hover .slider-thumb, -.vjs-frosted-skin .slider:focus-within .slider-thumb { - opacity: 1; -} -.vjs-frosted-skin .slider-track[data-orientation='horizontal'] .slider-thumb { - cursor: ew-resize; -} -.vjs-frosted-skin .slider-track[data-orientation='vertical'] .slider-thumb { - cursor: ns-resize; -} - -.vjs-frosted-skin .slider-pointer { - background-color: oklab(1 0 0 / 0.2); - border-radius: inherit; -} - -.vjs-frosted-skin .slider-progress { - background-color: oklab(1 0 0); - border-radius: inherit; -} - -.vjs-frosted-skin .media-preview-time-display { - font-variant-numeric: tabular-nums; -} - -.vjs-frosted-skin .popup-animation { - transition-property: transform, scale, opacity, filter; - transition-duration: 200ms; - transform: scale(1); - transform-origin: bottom; - opacity: 1; - filter: blur(0px); -} -.vjs-frosted-skin .popup-animation[data-starting-style], -.vjs-frosted-skin .popup-animation[data-ending-style] { - transform: scale(0); - opacity: 0; - filter: blur(8px); -} -.vjs-frosted-skin .popup-animation[data-instant] { - transition-duration: 0ms; -} - -.vjs-frosted-skin .popover-popup { - padding: 0.75rem 0.25rem; - border-radius: calc(infinity * 1px); - margin: 0; - border: 0; -} - -/* Tooltip Component Styles */ -.vjs-frosted-skin .tooltip-popup { - color: oklab(1 0 0); - padding: 0.25rem 0.625rem; - border-radius: calc(infinity * 1px); - font-size: 0.75rem; -} - -.vjs-frosted-skin .tooltip { - display: none; - white-space: nowrap; -} - -.vjs-frosted-skin .tooltip-popup[data-paused] .play-tooltip, -.vjs-frosted-skin .tooltip-popup:not([data-paused]) .pause-tooltip { - display: block; -} - -.vjs-frosted-skin .tooltip-popup[data-fullscreen] .fullscreen-exit-tooltip, -.vjs-frosted-skin .tooltip-popup:not([data-fullscreen]) .fullscreen-enter-tooltip { - display: block; -} diff --git a/packages/__tech-preview__/examples/react/src/skins/ejected/minimal/MinimalSkin.tsx b/packages/__tech-preview__/examples/react/src/skins/ejected/minimal/MinimalSkin.tsx deleted file mode 100644 index 25db946c..00000000 --- a/packages/__tech-preview__/examples/react/src/skins/ejected/minimal/MinimalSkin.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import type { PropsWithChildren } from 'react'; - -import { - CurrentTimeDisplay, - DurationDisplay, - FullscreenButton, - MediaContainer, - MuteButton, - PlayButton, - Popover, - PreviewTimeDisplay, - TimeSlider, - Tooltip, - VolumeSlider, -} from '@videojs/react-preview'; -import { - FullscreenEnterAltIcon, - FullscreenExitAltIcon, - PauseIcon, - PlayIcon, - VolumeHighIcon, - VolumeLowIcon, - VolumeOffIcon, -} from '@videojs/react-preview/icons'; - -import './minimal.css'; - -type SkinProps = PropsWithChildren<{ - className?: string; -}>; - -export default function MinimalSkin({ children, className = '' }: SkinProps): JSX.Element { - return ( - - {/* FIXME: Mismatch between React 18 & 19 types */} - {children as any} - -
- -
- - - - - - - - - - Play - Pause - - - - -
- - - - / - - -
- - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Enter Fullscreen - Exit Fullscreen - - - -
-
- - ); -} diff --git a/packages/__tech-preview__/examples/react/src/skins/ejected/minimal/minimal.css b/packages/__tech-preview__/examples/react/src/skins/ejected/minimal/minimal.css deleted file mode 100644 index c8cada81..00000000 --- a/packages/__tech-preview__/examples/react/src/skins/ejected/minimal/minimal.css +++ /dev/null @@ -1,347 +0,0 @@ -.vjs-minimal-skin * { - box-sizing: border-box; -} - -.vjs-minimal-skin { - position: relative; - isolation: isolate; - container: root / inline-size; - overflow: clip; - border-radius: var(--minimal-border-radius, 0.75rem); - background: oklab(0 0 0); - font-family: ui-sans-serif, system-ui, sans-serif; - font-size: 0.8125rem; - line-height: 1.5; - -webkit-font-smoothing: auto; - -moz-osx-font-smoothing: auto; -} -.vjs-minimal-skin::after { - content: ''; - position: absolute; - pointer-events: none; - border-radius: inherit; - z-index: 10; - inset: 0; - box-shadow: inset 0 0 0 1px oklab(0 0 0 / 0.15); -} -@media (prefers-color-scheme: dark) { - .vjs-minimal-skin::after { - box-shadow: inset 0 0 0 1px oklab(1 0 0 / 0.15); - } -} - -/* Fullscreen */ -.vjs-minimal-skin:fullscreen { - border-radius: 0; -} - -.vjs-minimal-skin > ::slotted([slot='media']) { - display: block; - width: 100%; - height: 100%; -} - -/* Media Container UI Overlay Styling */ -.vjs-minimal-skin > .overlay { - position: absolute; - inset: 0; - display: flex; - flex-flow: column nowrap; - align-items: start; - pointer-events: none; - border-radius: inherit; - background-image: linear-gradient(to top, oklab(0 0 0 / 0.7), oklab(0 0 0 / 0.5) 7.5rem, rgba(0, 0, 0, 0)); - transition-property: opacity; - transition-duration: 75ms; - transition-delay: 0ms; - transition-timing-function: ease-out; -} -.vjs-minimal-skin[data-controls='hidden'] > .overlay { - opacity: 0; - transition-duration: 500ms; - transition-delay: 500ms; -} - -/* Media Control Bar UI/Styles */ -.vjs-minimal-skin .control-bar { - position: absolute; - bottom: 0; - inset-inline: 0; - padding: 2rem 0.375rem 0.375rem 0.375rem; - display: flex; - align-items: center; - gap: 0.5rem; - will-change: transform, filter, opacity; - transition-property: transform, filter, opacity; - transition-delay: 0ms; - transition-duration: 75ms; - transition-timing-function: ease-out; - color: oklab(1 0 0); -} -.vjs-minimal-skin[data-controls='hidden'] > .control-bar { - opacity: 0; - transform: translateY(100%); - filter: blur(8px); - transition-delay: 500ms; - transition-duration: 500ms; - pointer-events: none; -} -@container root (width > 640px) { - .vjs-minimal-skin .control-bar { - padding: 2.5rem 0.75rem 0.75rem 0.75rem; - gap: 0.875rem; - } -} - -/* Time Display Styling */ -.vjs-minimal-skin .time-display-group { - display: flex; - align-items: center; - gap: 0.25rem; -} -.vjs-minimal-skin .duration-display { - display: contents; - color: oklab(1 0 0 / 0.5); -} -.vjs-minimal-skin .time-display { - text-shadow: 0 1px 0 oklab(0 0 0 / 0.2); - font-variant-numeric: tabular-nums; -} - -.vjs-minimal-skin .button-group { - display: flex; - align-items: center; - gap: 0.375rem; -} - -/* Generic Media Button Styling */ -.vjs-minimal-skin .button { - display: grid; - flex-shrink: 0; - padding: 0.625rem; - cursor: pointer; - background: transparent; - border: none; - border-radius: 0.375rem; - color: oklab(1 0 0); - user-select: none; - outline: 2px solid transparent; - outline-offset: -2px; - transition-property: background-color, color, outline-offset; - transition-duration: 150ms; - transition-timing-function: ease-out; -} -.vjs-minimal-skin .button:hover, -.vjs-minimal-skin .button:focus-visible, -.vjs-minimal-skin .button[aria-expanded='true'] { - color: oklab(1 0 0 / 0.8); - text-decoration: none; -} -.vjs-minimal-skin .button:focus-visible { - outline-color: oklab(1 0 0); - outline-offset: 2px; -} -.vjs-minimal-skin .button[disabled] { - cursor: not-allowed; - opacity: 0.5; - filter: grayscale(1); -} - -.vjs-minimal-skin .button .icon { - grid-area: 1 / 1; - width: 18px; - height: 18px; - filter: drop-shadow(0 1px 0 oklab(0 0 0 / 0.4)); -} - -/* Media Play Button UI/Styles */ -.vjs-minimal-skin .play-button .icon { - opacity: 0; - transition: opacity 150ms linear; -} - -.vjs-minimal-skin .play-button:not([data-paused]) .pause-icon, -.vjs-minimal-skin .play-button[data-paused] .play-icon { - opacity: 1; -} - -/* Media Fullscreen Button UI/Styles */ -.vjs-minimal-skin .fullscreen-button .icon { - display: none; -} -.vjs-minimal-skin .fullscreen-button:not([data-fullscreen]) .fullscreen-enter-icon, -.vjs-minimal-skin .fullscreen-button[data-fullscreen] .fullscreen-exit-icon { - display: inline; -} - -/* One way to define the "default visible" icon (CJP) */ -.vjs-minimal-skin .mute-button .icon { - display: none; -} -.vjs-minimal-skin .mute-button:not([data-volume-level]) .volume-low-icon, -.vjs-minimal-skin .mute-button[data-volume-level='high'] .volume-high-icon, -.vjs-minimal-skin .mute-button[data-volume-level='medium'] .volume-high-icon, -.vjs-minimal-skin .mute-button[data-volume-level='low'] .volume-low-icon, -.vjs-minimal-skin .mute-button[data-volume-level='off'] .volume-off-icon { - display: inline; -} - -/* TimeSlider Component Styles */ -.vjs-minimal-skin .slider { - flex: 1; - display: flex; - align-items: center; - justify-content: center; - position: relative; - border-radius: calc(infinity * 1px); - outline: none; -} - -/* Horizontal orientation styles */ -.vjs-minimal-skin .slider[data-orientation='horizontal'] { - min-width: 5rem; - width: 100%; - height: 1.25rem; -} - -/* Vertical orientation styles */ -.vjs-minimal-skin .slider[data-orientation='vertical'] { - height: 4.5rem; - width: 1.25rem; -} - -.vjs-minimal-skin .slider-track { - position: relative; - isolation: isolate; - background-color: oklab(1 0 0 / 0.2); - border-radius: inherit; - overflow: hidden; - user-select: none; - outline: 2px solid transparent; - outline-offset: -2px; - transition: outline-offset 150ms ease-out; - box-shadow: 0 0 0 1px oklab(0 0 0 / 0.05); -} - -/* Horizontal track styles */ -.vjs-minimal-skin .slider-track[data-orientation='horizontal'] { - width: 100%; - height: 0.1875rem; -} - -/* Vertical track styles */ -.vjs-minimal-skin .slider-track[data-orientation='vertical'] { - width: 0.1875rem; - height: 100%; -} - -.vjs-minimal-skin .slider:focus-visible .slider-track { - outline-color: oklab(1 0 0); - outline-offset: 6px; -} - -.vjs-minimal-skin .slider-thumb { - width: 0.75rem; - height: 0.75rem; - background-color: oklab(1 0 0); - border-radius: calc(infinity * 1px); - user-select: none; - z-index: 10; - box-shadow: - 0 0 0 1px oklab(0 0 0 / 0.1), - 0 1px 3px 0 oklab(0 0 0 / 0.15), - 0 1px 2px -1px oklab(0 0 0 / 0.15); - opacity: 0; - scale: 0.7; - transform-origin: center; - transition-property: opacity, scale; - transition-duration: 150ms; - transition-timing-function: ease-out; -} -.vjs-minimal-skin .slider:hover .slider-thumb, -.vjs-minimal-skin .slider:focus-within .slider-thumb { - opacity: 1; - scale: 1; -} -.vjs-minimal-skin .slider-track[data-orientation='horizontal'] .slider-thumb { - cursor: ew-resize; -} -.vjs-minimal-skin .slider-track[data-orientation='vertical'] .slider-thumb { - cursor: ns-resize; -} - -.vjs-minimal-skin .slider-pointer { - display: none; -} - -.vjs-minimal-skin .slider-progress { - background-color: oklab(1 0 0); - border-radius: inherit; -} - -.vjs-minimal-skin .media-preview-time-display { - font-variant-numeric: tabular-nums; -} - -.vjs-minimal-skin .popup-animation { - transition-property: transform, scale, opacity, filter; - transition-duration: 200ms; - transform: scale(1); - transform-origin: bottom; - opacity: 1; - filter: blur(0px); -} -.vjs-minimal-skin .popup-animation[data-starting-style], -.vjs-minimal-skin .popup-animation[data-ending-style] { - transform: scale(0); - opacity: 0; - filter: blur(8px); -} -.vjs-minimal-skin .popup-animation[data-instant] { - transition-duration: 0ms; -} - -.vjs-minimal-skin .popover-popup { - padding: 0.5rem; - margin: 0; - border: 0; - background: transparent; -} - -/* Tooltip Component Styles */ -.vjs-minimal-skin .tooltip-popup { - color: oklab(1 0 0); - padding: 0.25rem 0.5rem; - border-radius: 0.25rem; - font-size: 0.75rem; - background-color: oklab(1 0 0 / 0.1); - backdrop-filter: blur(64px) brightness(0.9) saturate(1.5); - box-shadow: - 0 4px 6px -1px oklab(0 0 0 / 0.1), - 0 2px 4px -2px oklab(0 0 0 / 0.1); -} -@media (prefers-reduced-transparency: reduce) { - .vjs-minimal-skin .tooltip-popup { - background-color: oklab(0 0 0 / 0.7); - } -} -@media (prefers-contrast: more) { - .vjs-minimal-skin .tooltip-popup { - background-color: oklab(0 0 0 / 0.9); - } -} - -.vjs-minimal-skin .tooltip { - display: none; - white-space: nowrap; -} - -.vjs-minimal-skin .tooltip-popup[data-paused] .play-tooltip, -.vjs-minimal-skin .tooltip-popup:not([data-paused]) .pause-tooltip { - display: block; -} - -.vjs-minimal-skin .tooltip-popup[data-fullscreen] .fullscreen-exit-tooltip, -.vjs-minimal-skin .tooltip-popup:not([data-fullscreen]) .fullscreen-enter-tooltip { - display: block; -} diff --git a/packages/__tech-preview__/examples/react/src/skins/imported/FrostedSkin.tsx b/packages/__tech-preview__/examples/react/src/skins/imported/FrostedSkin.tsx deleted file mode 100644 index 2ecc422e..00000000 --- a/packages/__tech-preview__/examples/react/src/skins/imported/FrostedSkin.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import '@videojs/react-preview/skins/frosted.css'; - -export { FrostedSkin as default } from '@videojs/react-preview'; diff --git a/packages/__tech-preview__/examples/react/src/skins/imported/MinimalSkin.tsx b/packages/__tech-preview__/examples/react/src/skins/imported/MinimalSkin.tsx deleted file mode 100644 index 73706afa..00000000 --- a/packages/__tech-preview__/examples/react/src/skins/imported/MinimalSkin.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import '@videojs/react-preview/skins/minimal.css'; - -export { MinimalSkin as default } from '@videojs/react-preview'; diff --git a/packages/__tech-preview__/examples/react/tsconfig.json b/packages/__tech-preview__/examples/react/tsconfig.json deleted file mode 100644 index 7c13dda8..00000000 --- a/packages/__tech-preview__/examples/react/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "jsx": "react-jsx", - "jsxImportSource": "react", - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "baseUrl": ".", - "paths": { - "@/*": ["src/*"] - } - }, - "include": ["src", "vite-env.d.ts"] -} diff --git a/packages/__tech-preview__/examples/react/vercel.json b/packages/__tech-preview__/examples/react/vercel.json deleted file mode 100644 index 0f32683a..00000000 --- a/packages/__tech-preview__/examples/react/vercel.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }] -} diff --git a/packages/__tech-preview__/examples/react/vite-env.d.ts b/packages/__tech-preview__/examples/react/vite-env.d.ts deleted file mode 100644 index 11f02fe2..00000000 --- a/packages/__tech-preview__/examples/react/vite-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/packages/__tech-preview__/examples/react/vite.config.ts b/packages/__tech-preview__/examples/react/vite.config.ts deleted file mode 100644 index 5baceff2..00000000 --- a/packages/__tech-preview__/examples/react/vite.config.ts +++ /dev/null @@ -1,22 +0,0 @@ -import path from 'node:path'; - -import tailwindcss from '@tailwindcss/vite'; -import react from '@vitejs/plugin-react'; -import { defineConfig } from 'vite'; - -// https://vitejs.dev/config -export default defineConfig({ - plugins: [react(), tailwindcss()], - resolve: { - alias: { - '@': path.resolve(__dirname, './src'), - }, - dedupe: ['react', 'react-dom'], - }, - server: { - port: 5173, - }, - optimizeDeps: { - exclude: ['@vjs/react'], - }, -}); diff --git a/packages/__tech-preview__/html/CHANGELOG.md b/packages/__tech-preview__/html/CHANGELOG.md deleted file mode 100644 index e823ee1f..00000000 --- a/packages/__tech-preview__/html/CHANGELOG.md +++ /dev/null @@ -1,232 +0,0 @@ -# Changelog - -## [0.1.0-preview.10](https://github.com/videojs/v10/compare/@videojs/html@0.1.0-preview.9...@videojs/html@0.1.0-preview.10) (2025-12-06) - - -### Features - -* add console banner ([#186](https://github.com/videojs/v10/issues/186)) ([072b0de](https://github.com/videojs/v10/commit/072b0dece76289d7d4215907ce70b36debbb78bc)) -* add tooltip core ([#212](https://github.com/videojs/v10/issues/212)) ([cbf41ce](https://github.com/videojs/v10/commit/cbf41ce4c750cd5c3bb6cbf247bad91ccd578cd0)) - - -### Bug Fixes - -* add popover core, use in html and improve factory ([#204](https://github.com/videojs/v10/issues/204)) ([f3b1b19](https://github.com/videojs/v10/commit/f3b1b199173f3750bc05ad9063fcccbd4163b12b)) -* use popover core in react popover ([#208](https://github.com/videojs/v10/issues/208)) ([99fef78](https://github.com/videojs/v10/commit/99fef78f63e8dd121513b9cd20696a7d35603837)) - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/core bumped to 0.1.0-preview.10 - * @videojs/icons bumped to 0.1.0-preview.10 - * @videojs/utils bumped to 0.1.0-preview.10 - -## [0.1.0-preview.9](https://github.com/videojs/v10/compare/@videojs/html@0.1.0-preview.8...@videojs/html@0.1.0-preview.9) (2025-11-18) - - -### Bug Fixes - -* clean up core, less seams in wrappers ([#197](https://github.com/videojs/v10/issues/197)) ([601a5a3](https://github.com/videojs/v10/commit/601a5a37939605ca38f81a825fd02934e7928c39)) - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/core bumped to 0.1.0-preview.9 - * @videojs/icons bumped to 0.1.0-preview.9 - * @videojs/utils bumped to 0.1.0-preview.9 - -## [0.1.0-preview.8](https://github.com/videojs/v10/compare/@videojs/html@0.1.0-preview.7...@videojs/html@0.1.0-preview.8) (2025-11-12) - - -### Miscellaneous Chores - -* **@videojs/html:** Synchronize videojs versions - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/core bumped to 0.1.0-preview.8 - * @videojs/icons bumped to 0.1.0-preview.8 - * @videojs/utils bumped to 0.1.0-preview.8 - -## [0.1.0-preview.7](https://github.com/videojs/v10/compare/@videojs/html@0.1.0-preview.6...@videojs/html@0.1.0-preview.7) (2025-11-11) - - -### Features - -* **react:** use popover and anchor position API ([#178](https://github.com/videojs/v10/issues/178)) ([f513c74](https://github.com/videojs/v10/commit/f513c74a6cebc09ac76512bb86af10a33350f900)) -* use anchor API for html elements ([#174](https://github.com/videojs/v10/issues/174)) ([4a2d580](https://github.com/videojs/v10/commit/4a2d580bb3db35d17d89827326617d0bd2c54499)) - - -### Bug Fixes - -* **skins:** remove vjs- prefixed CSS custom properties ([#179](https://github.com/videojs/v10/issues/179)) ([a62623b](https://github.com/videojs/v10/commit/a62623ba41d294edead981a1b468f7f486c91625)) -* **skins:** slightly more idiomatic Tailwind, added custom properties ([#175](https://github.com/videojs/v10/issues/175)) ([694afb7](https://github.com/videojs/v10/commit/694afb7de992edbd667bc3d895df8bf198ccabb3)) - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/core bumped to 0.1.0-preview.7 - * @videojs/icons bumped to 0.1.0-preview.7 - * @videojs/utils bumped to 0.1.0-preview.7 - -## [0.1.0-preview.6](https://github.com/videojs/v10/compare/@videojs/html@0.1.0-preview.5...@videojs/html@0.1.0-preview.6) (2025-11-06) - - -### Bug Fixes - -* **react, html:** rename MediaProvider (and related) to VideoProvider ([#159](https://github.com/videojs/v10/issues/159)) ([f285573](https://github.com/videojs/v10/commit/f28557359c7dda0282f9d532fa4b2fce19766e40)) -* **site:** update discord link ([#170](https://github.com/videojs/v10/issues/170)) ([bb10294](https://github.com/videojs/v10/commit/bb10294419439fb02df650f3e7f7c5496ecc3a73)) - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/core bumped to 0.1.0-preview.6 - * @videojs/icons bumped to 0.1.0-preview.6 - * @videojs/utils bumped to 0.1.0-preview.6 - -## [0.1.0-preview.5](https://github.com/videojs/v10/compare/@videojs/html@0.1.0-preview.4...@videojs/html@0.1.0-preview.5) (2025-11-03) - - -### Features - -* update html tooltip API / use command attr ([#151](https://github.com/videojs/v10/issues/151)) ([1e7c8ae](https://github.com/videojs/v10/commit/1e7c8ae82e317bada5b7bc2346d5b73b5f9bf5b7)) - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/core bumped to 0.1.0-preview.5 - * @videojs/icons bumped to 0.1.0-preview.5 - * @videojs/utils bumped to 0.1.0-preview.5 - -## [0.1.0-preview.4](https://github.com/videojs/v10/compare/@videojs/html@0.1.0-preview.3...@videojs/html@0.1.0-preview.4) (2025-10-30) - - -### Features - -* **html:** add element registrations ([eaaa03c](https://github.com/videojs/v10/commit/eaaa03c284f9fc3c9069bedb74d95c8a951c1b56)) - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/core bumped to 0.1.0-preview.4 - * @videojs/icons bumped to 0.1.0-preview.4 - * @videojs/utils bumped to 0.1.0-preview.4 - -## [0.1.0-preview.3](https://github.com/videojs/v10/compare/@videojs/html@0.1.0-preview.2...@videojs/html@0.1.0-preview.3) (2025-10-29) - - -### Features - -* idiomatic html markup, use popover API, add safe polygon utility ([#143](https://github.com/videojs/v10/issues/143)) ([419911f](https://github.com/videojs/v10/commit/419911f2f2b9f505700f5becb623bfe12e3878aa)) -* **skins:** add html port of minimal skin ([#140](https://github.com/videojs/v10/issues/140)) ([47ac22e](https://github.com/videojs/v10/commit/47ac22e9b8a513c9640ed1eff20838d8cdb749ce)) -* **ui:** skin design improvements, add html frosted skin (WIP) ([#133](https://github.com/videojs/v10/issues/133)) ([75a90e7](https://github.com/videojs/v10/commit/75a90e71041c2d0bf434823e59dd04f86f89b0bb)) - - -### Bug Fixes - -* add aria-hidden to focus guards ([77182f1](https://github.com/videojs/v10/commit/77182f1ab349d3b31470d7301e3dc2fb94c1d6ac)) -* add aspect-ratio to demos ([#136](https://github.com/videojs/v10/issues/136)) ([e0a46de](https://github.com/videojs/v10/commit/e0a46de0ab56199afd37b24ebfce93ee85e2b699)) -* add viewport meta element ([#135](https://github.com/videojs/v10/issues/135)) ([3bd50eb](https://github.com/videojs/v10/commit/3bd50ebac887229199fa6997eb0aaec361075b1a)) -* **packages:** update version badges ([#138](https://github.com/videojs/v10/issues/138)) ([22d9cb6](https://github.com/videojs/v10/commit/22d9cb64f2e5b9601a2039bb166dbe3fee6a1b3e)) -* remove `show-remaining` in HTML example ([#137](https://github.com/videojs/v10/issues/137)) ([7dde67a](https://github.com/videojs/v10/commit/7dde67ac927d6aca0f6a9b219ff3f3dc38594805)) -* visually hidden focus guards ([#142](https://github.com/videojs/v10/issues/142)) ([956dad7](https://github.com/videojs/v10/commit/956dad7ff5731d7630526ec97e33c652f260bc13)) - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/core bumped to 0.1.0-preview.3 - * @videojs/icons bumped to 0.1.0-preview.3 - * @videojs/utils bumped to 0.1.0-preview.3 - -## [0.1.0-preview.2](https://github.com/videojs/v10/compare/@videojs/html@0.1.0-preview.1...@videojs/html@0.1.0-preview.2) (2025-10-25) - - -### Miscellaneous Chores - -* **@videojs/html:** Synchronize videojs versions - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/core bumped to 0.1.0-preview.2 - * @videojs/icons bumped to 0.1.0-preview.2 - * @videojs/utils bumped to 0.1.0-preview.2 - -## [0.1.0-preview.1](https://github.com/videojs/v10/compare/@videojs/html@0.1.0-preview.0...@videojs/html@0.1.0-preview.1) (2025-10-25) - - -### Features - -* add compound html timerange component ([#14](https://github.com/videojs/v10/issues/14)) ([017ecdb](https://github.com/videojs/v10/commit/017ecdbff991d140ea42e4a855269a54e0a19adc)) -* add core range, time and volume range ([#23](https://github.com/videojs/v10/issues/23)) ([687b765](https://github.com/videojs/v10/commit/687b7655b0b6356c28663ca85c8f6d25a1023c18)) -* add data style attributes to popover ([#62](https://github.com/videojs/v10/issues/62)) ([6d7325c](https://github.com/videojs/v10/commit/6d7325cedb182c37b955e729d32204e4afbba948)) -* add display click to play / pause ([#117](https://github.com/videojs/v10/issues/117)) ([4f06ef6](https://github.com/videojs/v10/commit/4f06ef6c7684fd7064ca76685003a1c38ebd09cd)) -* add focus state to sliders and volume slider ([#60](https://github.com/videojs/v10/issues/60)) ([f514051](https://github.com/videojs/v10/commit/f514051263f95d892315eed9d44b3d83829e5d4b)) -* add html preview time display ([#58](https://github.com/videojs/v10/issues/58)) ([871701a](https://github.com/videojs/v10/commit/871701a453e49192ef986d286aaf957c0f533a93)) -* add HTML tooltip component ([#40](https://github.com/videojs/v10/issues/40)) ([d2a0b27](https://github.com/videojs/v10/commit/d2a0b27272f1967e5f7cde2df12920af92e3c300)) -* add HTML vertical orientation to time and volume ([#32](https://github.com/videojs/v10/issues/32)) ([7b79854](https://github.com/videojs/v10/commit/7b798543042e53e3c0a718b54b6c69038bbc1fcd)) -* add keyboard control to sliders ([#115](https://github.com/videojs/v10/issues/115)) ([0a49026](https://github.com/videojs/v10/commit/0a4902623d58f51055b1cc65498a0e716533ec29)) -* add media-popover, cleanup html demo ([#34](https://github.com/videojs/v10/issues/34)) ([17b632f](https://github.com/videojs/v10/commit/17b632f5f9c7ad6c4d84115e7ef42ee02e4ac2ba)) -* add showRemaining functionality to current time display ([ab07a8a](https://github.com/videojs/v10/commit/ab07a8a9a26f68066e292718f3cd17a2640c5477)) -* add tooltip transition status ([05988a8](https://github.com/videojs/v10/commit/05988a82ba10fe10eaa277d65bdf99cb022ff0e0)) -* add volume range compound component ([#19](https://github.com/videojs/v10/issues/19)) ([aefe890](https://github.com/videojs/v10/commit/aefe890fee93981542282087b2f2c7474f1b47e6)) -* **core,html,react:** implement VolumeRange component with integrated state management ([2282f47](https://github.com/videojs/v10/commit/2282f4799b1c3fc3c55473bdfc2def86384d5d19)) -* **html:** add fullscreen button component and icons ([63a3402](https://github.com/videojs/v10/commit/63a340213f483a809f736022a5eb8ef5683888e1)) -* **html:** implement duration display component ([e35dd44](https://github.com/videojs/v10/commit/e35dd44be86effee6e93882e88bb841e60c73fd9)) -* **html:** integrate fullscreen button into control bar and improve container lifecycle ([465342e](https://github.com/videojs/v10/commit/465342e4314e854332f5bdfee5eb6b4c113f1d87)) -* **icons:** implement shared SVG icon system across packages ([e0be58e](https://github.com/videojs/v10/commit/e0be58e094e65ea72af99b2c1e1d87c507c251bd)) -* implement current time display components ([5bd0a15](https://github.com/videojs/v10/commit/5bd0a154dbba01d2a5d11eb1f548fe4baa581675)) -* initialize Video.js 10 monorepo with core architecture ([4b0d84e](https://github.com/videojs/v10/commit/4b0d84e9c8adfa7401084389da5deb751420b629)) -* **media-store,html,react:** implement TimeRange component with hook-style architecture ([c29fd2c](https://github.com/videojs/v10/commit/c29fd2c2c1edd61c09a6683041c709a990d8a6f0)) -* migrate entire monorepo from tsc to tsup for production builds ([7403cc7](https://github.com/videojs/v10/commit/7403cc728119322888e527468a07a7634f43b32a)) -* **monorepo:** migrate prototype code to organized package structure ([4b472ec](https://github.com/videojs/v10/commit/4b472ec49cd91f0af61cb5aaa039d428982d3b91)) -* rename range to slider ([#46](https://github.com/videojs/v10/issues/46)) ([9c6eaef](https://github.com/videojs/v10/commit/9c6eaef2aa61771ae1407d0a594b3f790e0ff665)) -* **skins:** integrate duration display into default skins ([2ed2191](https://github.com/videojs/v10/commit/2ed219158ddf6f720ce1be8e328bec4603a7e847)) -* **ui:** port over default skin ([ff4ea36](https://github.com/videojs/v10/commit/ff4ea3693e63ab3b5a728988ca44f3bab669e8ff)) -* **ui:** port over default skin ([9950945](https://github.com/videojs/v10/commit/995094500823e1063e7ae291c9a2ea9a4aa74847)) - - -### Bug Fixes - -* Clean up more typescript errors. ([87105db](https://github.com/videojs/v10/commit/87105db6be31038fc92862c240898984d02932eb)) -* design tweaks to toasted skin, lint rule tweaks ([#44](https://github.com/videojs/v10/issues/44)) ([3a0767c](https://github.com/videojs/v10/commit/3a0767c3407b2d6d8af3d3a8afd57b1e76efda85)) -* enable eslint & run eslint:fix ([#43](https://github.com/videojs/v10/issues/43)) ([5cb93a1](https://github.com/videojs/v10/commit/5cb93a14a7f47d66d5c71f9b82867621beda236c)) -* minimal volume slider bug & fix dev infinite bug ([#73](https://github.com/videojs/v10/issues/73)) ([591dab6](https://github.com/videojs/v10/commit/591dab66caf8829017688007320f92b7445c4baa)) -* refactor private fields to public with underscore convention ([3cffb4d](https://github.com/videojs/v10/commit/3cffb4d59a94ed2ac41acc7eff775ededef948e1)) -* rename attributes to kebab-case ([5195c09](https://github.com/videojs/v10/commit/5195c09af610625f10cb218d14a2a87fc1695701)) -* resolve @open-wc/context-protocol module resolution issues ([e5f23ed](https://github.com/videojs/v10/commit/e5f23ed2bbde6bd50f8b87d19733b25b98f78470)) -* resolve TypeScript build errors across packages ([374db7a](https://github.com/videojs/v10/commit/374db7afc07d6211bfd3f8079bbcd9613f3b69f3)) -* **root:** add videojs keyword to package.json ([4a9f8b2](https://github.com/videojs/v10/commit/4a9f8b2ad6fb27b463dcfe8d1a5fd883c9fa21d1)) -* seek jump back to current time ([#22](https://github.com/videojs/v10/issues/22)) ([a3f9630](https://github.com/videojs/v10/commit/a3f9630bd1eb34a16f339ffd30071b8adc864ca0)) -* skin syntax usage cleanup ([#48](https://github.com/videojs/v10/issues/48)) ([d4f3f4b](https://github.com/videojs/v10/commit/d4f3f4b75b2c94c47b18242f131ac7050eb54cfc)) -* **time-display:** clean up time utilities and simplify components ([597e79d](https://github.com/videojs/v10/commit/597e79d7fc12737353c8c9eb3f6e77ef0a04e9ed)) -* **typescript:** resolve declaration file generation for rollup packages ([#1](https://github.com/videojs/v10/issues/1)) ([69670e8](https://github.com/videojs/v10/commit/69670e8d7134db34aee665d8871cd17901625915)) -* **workspace:** convert pnpm workspace protocol to npm workspace syntax ([fc5a0e4](https://github.com/videojs/v10/commit/fc5a0e46fd15f30245cb743a8006fc097c5b890e)) - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/core bumped to 0.1.0-preview.1 - * @videojs/icons bumped to 0.1.0-preview.1 - * @videojs/utils bumped to 0.1.0-preview.1 diff --git a/packages/__tech-preview__/html/README.md b/packages/__tech-preview__/html/README.md deleted file mode 100644 index e0fdf5fe..00000000 --- a/packages/__tech-preview__/html/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# @videojs/html - -[![package-badge]][package] - -> **⚠️ Technical Preview - SUBJECT TO CHANGE** Not recommended for production use. - -## Overview - -`@videojs/html` is a comprehensive library for building media players with vanilla JavaScript and Web Components. It provides a complete set of UI components, state management, and utilities for creating feature-rich, accessible video and audio players. - -## Community - -If you need help with anything related to Video.js 10, or if you'd like to casually chat with other -members: - -- [Join Discord Server][discord] -- [See GitHub Discussions][gh-discussions] - -## License - -[Apache-2.0](./LICENSE) - -[package]: https://www.npmjs.com/package/@videojs/html -[package-badge]: https://img.shields.io/npm/v/@videojs/html/next?label=@videojs/html@next -[discord]: https://discord.gg/JBqHh485uF -[gh-discussions]: https://github.com/videojs/v10/discussions diff --git a/packages/__tech-preview__/html/package.json b/packages/__tech-preview__/html/package.json deleted file mode 100644 index d113498a..00000000 --- a/packages/__tech-preview__/html/package.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "name": "@videojs/html-preview", - "type": "module", - "version": "0.1.0-preview.10", - "description": "HTML library for building media players", - "license": "Apache-2.0", - "keywords": [ - "media", - "player", - "html", - "ui", - "components", - "videojs" - ], - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "./store": { - "types": "./dist/store.d.ts", - "default": "./dist/store.js" - }, - "./icons": { - "types": "./dist/icons.d.ts", - "default": "./dist/icons.js" - }, - "./skins/frosted": { - "types": "./dist/skins/frosted.d.ts", - "default": "./dist/skins/frosted.js" - }, - "./skins/minimal": { - "types": "./dist/skins/minimal.d.ts", - "default": "./dist/skins/minimal.js" - }, - "./define/*": { - "types": "./dist/define/*.d.ts", - "default": "./dist/define/*.js" - } - }, - "main": "dist/index.js", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist" - ], - "scripts": { - "build": "tsdown", - "build:watch": "tsdown --watch --silent", - "dev": "pnpm run build:watch", - "test": "echo \"No tests yet\"", - "clean": "rm -rf dist" - }, - "dependencies": { - "@open-wc/context-protocol": "^0.0.9", - "@videojs/core-preview": "workspace:*", - "@videojs/icons-preview": "workspace:*", - "@videojs/utils-preview": "workspace:*" - }, - "devDependencies": { - "tsdown": "^0.15.9", - "typescript": "^5.9.3" - }, - "publishConfig": { - "access": "public" - } -} diff --git a/packages/__tech-preview__/html/src/define/index.ts b/packages/__tech-preview__/html/src/define/index.ts deleted file mode 100644 index 239d0bed..00000000 --- a/packages/__tech-preview__/html/src/define/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -// be sure to import video-provider first for proper context initialization -import './video-provider'; -import './media-container'; -import './media-play-button'; -import './media-mute-button'; -import './media-volume-slider'; -import './media-time-slider'; -import './media-fullscreen-button'; -import './media-duration-display'; -import './media-current-time-display'; -import './media-preview-time-display'; -import './media-popover'; -import './media-tooltip'; diff --git a/packages/__tech-preview__/html/src/define/media-container.ts b/packages/__tech-preview__/html/src/define/media-container.ts deleted file mode 100644 index 71965cf7..00000000 --- a/packages/__tech-preview__/html/src/define/media-container.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { MediaContainerElement } from '@/media/media-container'; -import { defineCustomElement } from '@/utils/custom-element'; - -defineCustomElement('media-container', MediaContainerElement); diff --git a/packages/__tech-preview__/html/src/define/media-current-time-display.ts b/packages/__tech-preview__/html/src/define/media-current-time-display.ts deleted file mode 100644 index 77661119..00000000 --- a/packages/__tech-preview__/html/src/define/media-current-time-display.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { CurrentTimeDisplayElement } from '@/elements/current-time-display'; -import { defineCustomElement } from '@/utils/custom-element'; - -defineCustomElement('media-current-time-display', CurrentTimeDisplayElement); diff --git a/packages/__tech-preview__/html/src/define/media-duration-display.ts b/packages/__tech-preview__/html/src/define/media-duration-display.ts deleted file mode 100644 index 1779d0ac..00000000 --- a/packages/__tech-preview__/html/src/define/media-duration-display.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { DurationDisplayElement } from '@/elements/duration-display'; -import { defineCustomElement } from '@/utils/custom-element'; - -defineCustomElement('media-duration-display', DurationDisplayElement); diff --git a/packages/__tech-preview__/html/src/define/media-fullscreen-button.ts b/packages/__tech-preview__/html/src/define/media-fullscreen-button.ts deleted file mode 100644 index 3f344805..00000000 --- a/packages/__tech-preview__/html/src/define/media-fullscreen-button.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { FullscreenButtonElement } from '@/elements/fullscreen-button'; -import { defineCustomElement } from '@/utils/custom-element'; - -defineCustomElement('media-fullscreen-button', FullscreenButtonElement); diff --git a/packages/__tech-preview__/html/src/define/media-mute-button.ts b/packages/__tech-preview__/html/src/define/media-mute-button.ts deleted file mode 100644 index f9025341..00000000 --- a/packages/__tech-preview__/html/src/define/media-mute-button.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { MuteButtonElement } from '@/elements/mute-button'; -import { defineCustomElement } from '@/utils/custom-element'; - -defineCustomElement('media-mute-button', MuteButtonElement); diff --git a/packages/__tech-preview__/html/src/define/media-play-button.ts b/packages/__tech-preview__/html/src/define/media-play-button.ts deleted file mode 100644 index 534922f5..00000000 --- a/packages/__tech-preview__/html/src/define/media-play-button.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { PlayButtonElement } from '@/elements/play-button'; -import { defineCustomElement } from '@/utils/custom-element'; - -defineCustomElement('media-play-button', PlayButtonElement); diff --git a/packages/__tech-preview__/html/src/define/media-popover.ts b/packages/__tech-preview__/html/src/define/media-popover.ts deleted file mode 100644 index e5392ec9..00000000 --- a/packages/__tech-preview__/html/src/define/media-popover.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { PopoverElement } from '@/elements/popover'; -import { defineCustomElement } from '@/utils/custom-element'; - -defineCustomElement('media-popover', PopoverElement); diff --git a/packages/__tech-preview__/html/src/define/media-preview-time-display.ts b/packages/__tech-preview__/html/src/define/media-preview-time-display.ts deleted file mode 100644 index 912f7b30..00000000 --- a/packages/__tech-preview__/html/src/define/media-preview-time-display.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { PreviewTimeDisplayElement } from '@/elements/preview-time-display'; -import { defineCustomElement } from '@/utils/custom-element'; - -defineCustomElement('media-preview-time-display', PreviewTimeDisplayElement); diff --git a/packages/__tech-preview__/html/src/define/media-time-slider.ts b/packages/__tech-preview__/html/src/define/media-time-slider.ts deleted file mode 100644 index df0dd761..00000000 --- a/packages/__tech-preview__/html/src/define/media-time-slider.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { TimeSliderPointerElement, TimeSliderProgressElement, TimeSliderRootElement, TimeSliderThumbElement, TimeSliderTrackElement } from '@/elements/time-slider'; -import { defineCustomElement } from '@/utils/custom-element'; - -defineCustomElement('media-time-slider', TimeSliderRootElement); -defineCustomElement('media-time-slider-track', TimeSliderTrackElement); -defineCustomElement('media-time-slider-progress', TimeSliderProgressElement); -defineCustomElement('media-time-slider-pointer', TimeSliderPointerElement); -defineCustomElement('media-time-slider-thumb', TimeSliderThumbElement); diff --git a/packages/__tech-preview__/html/src/define/media-tooltip.ts b/packages/__tech-preview__/html/src/define/media-tooltip.ts deleted file mode 100644 index 253c3b8a..00000000 --- a/packages/__tech-preview__/html/src/define/media-tooltip.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { TooltipElement } from '@/elements/tooltip'; -import { defineCustomElement } from '@/utils/custom-element'; - -defineCustomElement('media-tooltip', TooltipElement); diff --git a/packages/__tech-preview__/html/src/define/media-volume-slider.ts b/packages/__tech-preview__/html/src/define/media-volume-slider.ts deleted file mode 100644 index 6a9bf90c..00000000 --- a/packages/__tech-preview__/html/src/define/media-volume-slider.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { VolumeSliderIndicatorElement, VolumeSliderRootElement, VolumeSliderThumbElement, VolumeSliderTrackElement } from '@/elements/volume-slider'; -import { defineCustomElement } from '@/utils/custom-element'; - -defineCustomElement('media-volume-slider', VolumeSliderRootElement); -defineCustomElement('media-volume-slider-track', VolumeSliderTrackElement); -defineCustomElement('media-volume-slider-indicator', VolumeSliderIndicatorElement); -defineCustomElement('media-volume-slider-thumb', VolumeSliderThumbElement); diff --git a/packages/__tech-preview__/html/src/define/video-provider.ts b/packages/__tech-preview__/html/src/define/video-provider.ts deleted file mode 100644 index dece3a47..00000000 --- a/packages/__tech-preview__/html/src/define/video-provider.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { VideoProviderElement } from '@/media/video-provider'; -import { defineCustomElement } from '@/utils/custom-element'; - -defineCustomElement('video-provider', VideoProviderElement); diff --git a/packages/__tech-preview__/html/src/elements/button.ts b/packages/__tech-preview__/html/src/elements/button.ts deleted file mode 100644 index fa55d4e2..00000000 --- a/packages/__tech-preview__/html/src/elements/button.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { namedNodeMapToObject } from '@videojs/utils-preview/dom'; - -export function getTemplateHTML( - this: typeof ButtonElement, - _attrs: Record, - _props: Record = {}, -): string { - return /* html */ ` - - - - `; -} - -export class ButtonElement extends HTMLElement { - static shadowRootOptions = { - mode: 'open' as ShadowRootMode, - }; - - static getTemplateHTML: typeof getTemplateHTML = getTemplateHTML; - - static observedAttributes: string[] = ['commandfor']; - - constructor() { - super(); - - if (!this.shadowRoot) { - // Set up the Shadow DOM if not using Declarative Shadow DOM. - this.attachShadow((this.constructor as typeof ButtonElement).shadowRootOptions); - - const attrs = namedNodeMapToObject(this.attributes); - const html = (this.constructor as typeof ButtonElement).getTemplateHTML(attrs); - // From MDN: setHTMLUnsafe should be used instead of ShadowRoot.innerHTML - // when a string of HTML may contain declarative shadow roots. - const shadowRoot = this.shadowRoot as unknown as ShadowRoot; - shadowRoot.setHTMLUnsafe ? shadowRoot.setHTMLUnsafe(html) : (shadowRoot.innerHTML = html); - } - - this.addEventListener('click', this); - this.addEventListener('keydown', this); - } - - attributeChangedCallback(name: string, _oldValue: string, newValue: string): void { - if (name === 'commandfor') { - this.style.setProperty('anchor-name', `--${newValue}`); - } - } - - handleEvent(event: Event): void { - const { type } = event; - if (type === 'keydown') { - this.#handleKeyDown(event as KeyboardEvent); - } - } - - #handleKeyDown = (event: KeyboardEvent): void => { - const { metaKey, altKey, key } = event; - if (metaKey || altKey || !['Enter', ' '].includes(key)) { - this.removeEventListener('keyup', this.#handleKeyUp); - return; - } - this.addEventListener('keyup', this.#handleKeyUp, { once: true }); - }; - - #handleKeyUp = (_event: KeyboardEvent): void => { - this.handleEvent({ type: 'click' } as Event); - }; -} diff --git a/packages/__tech-preview__/html/src/elements/current-time-display.ts b/packages/__tech-preview__/html/src/elements/current-time-display.ts deleted file mode 100644 index 2e9aed89..00000000 --- a/packages/__tech-preview__/html/src/elements/current-time-display.ts +++ /dev/null @@ -1,56 +0,0 @@ -import type { CurrentTimeDisplayState } from '@videojs/core-preview/store'; -import type { ConnectedComponentConstructor, PropsHook, StateHook } from '../utils/component-factory'; - -import { currentTimeDisplayStateDefinition } from '@videojs/core-preview/store'; -import { formatDisplayTime } from '@videojs/utils-preview'; -import { toConnectedHTMLComponent } from '../utils/component-factory'; - -export const getCurrentTimeDisplayState: StateHook = (_element, mediaStore) => { - return { - ...currentTimeDisplayStateDefinition.stateTransform(mediaStore.getState()), - // Current time display is read-only, so no request methods needed - }; -}; - -export const getCurrentTimeDisplayProps: PropsHook = (_element, _state) => { - return {}; -}; - -export class CurrentTimeDisplay extends HTMLElement { - static shadowRootOptions = { - mode: 'open' as ShadowRootMode, - }; - - static observedAttributes: string[] = ['show-remaining']; - - constructor() { - super(); - - if (!this.shadowRoot) { - this.attachShadow((this.constructor as typeof CurrentTimeDisplay).shadowRootOptions); - } - } - - get showRemaining(): boolean { - return this.hasAttribute('show-remaining'); - } - - _update(_props: any, state: CurrentTimeDisplayState): void { - /** @TODO Should this live here or elsewhere? (CJP) */ - const timeLabel - = this.showRemaining && state.duration != null && state.currentTime != null - ? formatDisplayTime(-(state.duration - state.currentTime)) - : formatDisplayTime(state.currentTime); - - if (this.shadowRoot) { - this.shadowRoot.textContent = timeLabel; - } - } -} - -export const CurrentTimeDisplayElement: ConnectedComponentConstructor = toConnectedHTMLComponent( - CurrentTimeDisplay, - getCurrentTimeDisplayState, - getCurrentTimeDisplayProps, - 'CurrentTimeDisplay', -); diff --git a/packages/__tech-preview__/html/src/elements/duration-display.ts b/packages/__tech-preview__/html/src/elements/duration-display.ts deleted file mode 100644 index bbd8668d..00000000 --- a/packages/__tech-preview__/html/src/elements/duration-display.ts +++ /dev/null @@ -1,65 +0,0 @@ -import type { DurationDisplayState } from '@videojs/core-preview/store'; -import type { ConnectedComponentConstructor, PropsHook, StateHook } from '../utils/component-factory'; - -import { durationDisplayStateDefinition } from '@videojs/core-preview/store'; - -import { formatDisplayTime } from '@videojs/utils-preview'; -import { namedNodeMapToObject } from '@videojs/utils-preview/dom'; -import { toConnectedHTMLComponent } from '../utils/component-factory'; - -export function getTemplateHTML( - this: typeof DurationDisplay, - _attrs: Record, - _props: Record = {}, -) { - return /* html */ ` - - `; -} - -export class DurationDisplay extends HTMLElement { - static shadowRootOptions = { - mode: 'open' as ShadowRootMode, - }; - - static getTemplateHTML: typeof getTemplateHTML = getTemplateHTML; - - constructor() { - super(); - - if (!this.shadowRoot) { - this.attachShadow((this.constructor as typeof DurationDisplay).shadowRootOptions); - - const attrs = namedNodeMapToObject(this.attributes); - const html = (this.constructor as typeof DurationDisplay).getTemplateHTML(attrs); - const shadowRoot = this.shadowRoot as unknown as ShadowRoot; - shadowRoot.setHTMLUnsafe ? shadowRoot.setHTMLUnsafe(html) : (shadowRoot.innerHTML = html); - } - } - - _update(_props: any, state: DurationDisplayState): void { - // Update the span content with formatted duration - const spanElement = this.shadowRoot?.querySelector('span') as HTMLElement; - if (spanElement) { - spanElement.textContent = formatDisplayTime(state.duration); - } - } -} - -export const getDurationDisplayState: StateHook = (_element, mediaStore) => { - return { - ...durationDisplayStateDefinition.stateTransform(mediaStore.getState()), - // Duration display is read-only, so no request methods needed - }; -}; - -export const getDurationDisplayProps: PropsHook = (_element, _state) => { - return {}; -}; - -export const DurationDisplayElement: ConnectedComponentConstructor = toConnectedHTMLComponent( - DurationDisplay, - getDurationDisplayState, - getDurationDisplayProps, - 'DurationDisplay', -); diff --git a/packages/__tech-preview__/html/src/elements/fullscreen-button.ts b/packages/__tech-preview__/html/src/elements/fullscreen-button.ts deleted file mode 100644 index e7c8a772..00000000 --- a/packages/__tech-preview__/html/src/elements/fullscreen-button.ts +++ /dev/null @@ -1,67 +0,0 @@ -import type { FullscreenButtonState } from '@videojs/core-preview/store'; -import type { Prettify } from '../types'; -import type { ConnectedComponentConstructor, PropsHook, StateHook } from '../utils/component-factory'; - -import { fullscreenButtonStateDefinition } from '@videojs/core-preview/store'; - -import { memoize } from '@videojs/utils-preview'; -import { toConnectedHTMLComponent } from '../utils/component-factory'; -import { ButtonElement } from './button'; - -type FullscreenButtonStateWithMethods = Prettify>; - -const fullscreenButtonCreateRequestMethods = memoize(fullscreenButtonStateDefinition.createRequestMethods); - -/** - * FullscreenButton state hook - equivalent to React's useFullscreenButtonState - * Handles media store state subscription and transformation - */ -export const getFullscreenButtonState: StateHook = (_element, mediaStore) => { - return { - ...fullscreenButtonStateDefinition.stateTransform(mediaStore.getState()), - ...fullscreenButtonCreateRequestMethods(mediaStore.dispatch), - }; -}; - -export const getFullscreenButtonProps: PropsHook = (_element, state) => { - const baseProps: Record = { - /** data attributes/props */ - 'data-fullscreen': state.fullscreen, - /** @TODO Need another state provider in core for i18n (CJP) */ - /** aria attributes/props */ - role: 'button', - tabindex: '0', - 'aria-label': state.fullscreen ? 'exit fullscreen' : 'enter fullscreen', - /** tooltip */ - 'data-tooltip': state.fullscreen ? 'Exit Fullscreen' : 'Enter Fullscreen', - /** @TODO Figure out how we want to handle attr overrides (e.g. aria-label) (CJP) */ - /** external props spread last to allow for overriding */ - // ...props, - }; - return baseProps; -}; - -export class FullscreenButton extends ButtonElement { - _state: FullscreenButtonStateWithMethods | undefined; - - handleEvent(event: Event): void { - super.handleEvent(event); - - const { type } = event; - const state = this._state; - if (state && type === 'click') { - if (state.fullscreen) { - state.requestExitFullscreen(); - } else { - state.requestEnterFullscreen(); - } - } - } -} - -export const FullscreenButtonElement: ConnectedComponentConstructor = toConnectedHTMLComponent( - FullscreenButton, - getFullscreenButtonState, - getFullscreenButtonProps, - 'FullscreenButton', -); diff --git a/packages/__tech-preview__/html/src/elements/mute-button.ts b/packages/__tech-preview__/html/src/elements/mute-button.ts deleted file mode 100644 index 29e62dc0..00000000 --- a/packages/__tech-preview__/html/src/elements/mute-button.ts +++ /dev/null @@ -1,67 +0,0 @@ -import type { MuteButtonState } from '@videojs/core-preview/store'; -import type { Prettify } from '../types'; -import type { ConnectedComponentConstructor, PropsHook, StateHook } from '../utils/component-factory'; - -import { muteButtonStateDefinition } from '@videojs/core-preview/store'; - -import { memoize } from '@videojs/utils-preview'; -import { toConnectedHTMLComponent } from '../utils/component-factory'; -import { ButtonElement } from './button'; - -type MuteButtonStateWithMethods = Prettify>; - -const muteButtonCreateRequestMethods = memoize(muteButtonStateDefinition.createRequestMethods); - -export const getMuteButtonState: StateHook = (_element, mediaStore) => { - return { - ...muteButtonStateDefinition.stateTransform(mediaStore.getState()), - ...muteButtonCreateRequestMethods(mediaStore.dispatch), - }; -}; - -export const getMuteButtonProps: PropsHook = (_element, state) => { - const baseProps: Record = { - /** data attributes/props */ - 'data-muted': state.muted, - 'data-volume-level': state.volumeLevel, - /** @TODO Need another state provider in core for i18n (CJP) */ - /** aria attributes/props */ - role: 'button', - tabindex: '0', - 'aria-label': state.muted ? 'unmute' : 'mute', - /** tooltip */ - 'data-tooltip': state.muted ? 'Unmute' : 'Mute', - /** @TODO Figure out how we want to handle attr overrides (e.g. aria-label) (CJP) */ - /** external props spread last to allow for overriding */ - // ...props, - }; - return baseProps; -}; - -export class MuteButton extends ButtonElement { - _state: MuteButtonStateWithMethods | undefined; - - handleEvent(event: Event): void { - super.handleEvent(event); - - const { type } = event; - const state = this._state; - - if (state) { - if (type === 'click') { - if (state.volumeLevel === 'off') { - state.requestUnmute(); - } else { - state.requestMute(); - } - } - } - } -} - -export const MuteButtonElement: ConnectedComponentConstructor = toConnectedHTMLComponent( - MuteButton, - getMuteButtonState, - getMuteButtonProps, - 'MuteButton', -); diff --git a/packages/__tech-preview__/html/src/elements/play-button.ts b/packages/__tech-preview__/html/src/elements/play-button.ts deleted file mode 100644 index c26499bc..00000000 --- a/packages/__tech-preview__/html/src/elements/play-button.ts +++ /dev/null @@ -1,67 +0,0 @@ -import type { PlayButtonState } from '@videojs/core-preview/store'; -import type { Prettify } from '../types'; -import type { ConnectedComponentConstructor, PropsHook, StateHook } from '../utils/component-factory'; - -import { playButtonStateDefinition } from '@videojs/core-preview/store'; -import { memoize } from '@videojs/utils-preview'; -import { toConnectedHTMLComponent } from '../utils/component-factory'; -import { ButtonElement } from './button'; - -type PlayButtonStateWithMethods = Prettify>; - -const playButtonCreateRequestMethods = memoize(playButtonStateDefinition.createRequestMethods); - -/** - * PlayButton state hook - equivalent to React's usePlayButtonState - * Handles media store state subscription and transformation - */ -export const getPlayButtonState: StateHook = (_element, mediaStore) => { - return { - ...playButtonStateDefinition.stateTransform(mediaStore.getState()), - ...playButtonCreateRequestMethods(mediaStore.dispatch), - }; -}; - -export const getPlayButtonProps: PropsHook = (_element, state) => { - const baseProps: Record = { - /** data attributes/props */ - 'data-paused': state.paused, - /** @TODO Need another state provider in core for i18n (CJP) */ - /** aria attributes/props */ - role: 'button', - tabindex: '0', - 'aria-label': state.paused ? 'play' : 'pause', - /** tooltip */ - 'data-tooltip': state.paused ? 'Play' : 'Pause', - /** @TODO Figure out how we want to handle attr overrides (e.g. aria-label) (CJP) */ - /** external props spread last to allow for overriding */ - // ...props, - }; - - return baseProps; -}; - -export class PlayButton extends ButtonElement { - _state: PlayButtonStateWithMethods | undefined; - - handleEvent(event: Event): void { - super.handleEvent(event); - - const { type } = event; - const state = this._state; - if (state && type === 'click') { - if (state.paused) { - state.requestPlay(); - } else { - state.requestPause(); - } - } - } -} - -export const PlayButtonElement: ConnectedComponentConstructor = toConnectedHTMLComponent( - PlayButton, - getPlayButtonState, - getPlayButtonProps, - 'PlayButton', -); diff --git a/packages/__tech-preview__/html/src/elements/popover.ts b/packages/__tech-preview__/html/src/elements/popover.ts deleted file mode 100644 index f3c23a81..00000000 --- a/packages/__tech-preview__/html/src/elements/popover.ts +++ /dev/null @@ -1,100 +0,0 @@ -import type { PopoverState as CorePopoverState } from '@videojs/core-preview'; -import type { Prettify } from '../types'; -import type { ConnectedComponentConstructor, PropsHook, StateHook } from '../utils/component-factory'; -import { Popover as CorePopover } from '@videojs/core-preview'; -import { getDocumentOrShadowRoot } from '@videojs/utils-preview/dom'; -import { getCoreState, getPropsFromAttrs, toConnectedHTMLComponent } from '../utils/component-factory'; - -type Placement = CorePopoverState['placement']; - -export type PopoverState = Prettify>; - -export const getPopoverState: StateHook = (element, _mediaStore) => { - const coreState = getCoreState(CorePopover, getPropsFromAttrs(element)); - return { - ...coreState, - }; -}; - -export const getPopoverProps: PropsHook = (element, state) => { - if (state._popoverElement !== element) { - state._setPopoverElement(element); - } - - const triggerElement = getDocumentOrShadowRoot(element)?.querySelector( - `[commandfor="${element.id}"]`, - ) as HTMLElement | null; - - if (state._triggerElement !== triggerElement) { - state._setTriggerElement(triggerElement); - } - - const mediaContainer = element.closest(element.collisionBoundary - ? `#${element.collisionBoundary}` - : 'media-container') as HTMLElement | null; - - if (state._collisionBoundaryElement !== mediaContainer) { - state._setCollisionBoundaryElement(mediaContainer); - } - - return { - 'data-side': state.placement, - 'data-starting-style': state._transitionStatus === 'initial', - 'data-open': state._transitionStatus === 'initial' || state._transitionStatus === 'open', - 'data-ending-style': state._transitionStatus === 'close' || state._transitionStatus === 'unmounted', - 'data-closed': state._transitionStatus === 'close' || state._transitionStatus === 'unmounted', - style: { - ...state._popoverStyle, - }, - }; -}; - -export class Popover extends HTMLElement { - static get observedAttributes(): string[] { - return [ - 'id', - 'open-on-hover', - 'delay', - 'close-delay', - 'side', - 'side-offset', - 'collision-padding', - 'collision-boundary', - ]; - } - - get openOnHover(): boolean { - return this.hasAttribute('open-on-hover'); - } - - get delay(): number { - return Number.parseInt(this.getAttribute('delay') ?? '0', 10); - } - - get closeDelay(): number { - return Number.parseInt(this.getAttribute('close-delay') ?? '0', 10); - } - - get side(): Placement { - return this.getAttribute('side') as Placement; - } - - get sideOffset(): number { - return Number.parseInt(this.getAttribute('side-offset') ?? '0', 10); - } - - get collisionPadding(): number { - return Number.parseInt(this.getAttribute('collision-padding') ?? '0', 10); - } - - get collisionBoundary(): string | null { - return this.getAttribute('collision-boundary'); - } -} - -export const PopoverElement: ConnectedComponentConstructor = toConnectedHTMLComponent( - Popover, - getPopoverState, - getPopoverProps, - 'Popover', -); diff --git a/packages/__tech-preview__/html/src/elements/preview-time-display.ts b/packages/__tech-preview__/html/src/elements/preview-time-display.ts deleted file mode 100644 index 979eb5e6..00000000 --- a/packages/__tech-preview__/html/src/elements/preview-time-display.ts +++ /dev/null @@ -1,54 +0,0 @@ -import type { PreviewTimeDisplayState } from '@videojs/core-preview/store'; -import type { ConnectedComponentConstructor, PropsHook, StateHook } from '../utils/component-factory'; - -import { previewTimeDisplayStateDefinition } from '@videojs/core-preview/store'; - -import { formatDisplayTime } from '@videojs/utils-preview'; -import { toConnectedHTMLComponent } from '../utils/component-factory'; - -export const getPreviewTimeDisplayState: StateHook = (_element, mediaStore) => { - return { - ...previewTimeDisplayStateDefinition.stateTransform(mediaStore.getState()), - // Preview time display is read-only, so no request methods needed - }; -}; - -export const getPreviewTimeDisplayProps: PropsHook = (_element, _state) => { - return {}; -}; - -export class PreviewTimeDisplay extends HTMLElement { - static shadowRootOptions = { - mode: 'open' as ShadowRootMode, - }; - - static observedAttributes: string[] = ['show-remaining']; - - constructor() { - super(); - - if (!this.shadowRoot) { - this.attachShadow((this.constructor as typeof PreviewTimeDisplay).shadowRootOptions); - } - } - - get showRemaining(): boolean { - return this.hasAttribute('show-remaining'); - } - - _update(_props: any, state: PreviewTimeDisplayState): void { - /** @TODO Should this live here or elsewhere? (CJP) */ - const timeLabel = formatDisplayTime(state.previewTime); - - if (this.shadowRoot) { - this.shadowRoot.textContent = timeLabel; - } - } -} - -export const PreviewTimeDisplayElement: ConnectedComponentConstructor = toConnectedHTMLComponent( - PreviewTimeDisplay, - getPreviewTimeDisplayState, - getPreviewTimeDisplayProps, - 'PreviewTimeDisplay', -); diff --git a/packages/__tech-preview__/html/src/elements/time-slider.ts b/packages/__tech-preview__/html/src/elements/time-slider.ts deleted file mode 100644 index 874c59b5..00000000 --- a/packages/__tech-preview__/html/src/elements/time-slider.ts +++ /dev/null @@ -1,242 +0,0 @@ -import type { Prettify } from '../types'; -import type { ConnectedComponentConstructor, PropsHook, StateHook } from '../utils/component-factory'; - -import { TimeSlider as CoreTimeSlider } from '@videojs/core-preview'; -import { timeSliderStateDefinition } from '@videojs/core-preview/store'; -import { memoize } from '@videojs/utils-preview'; - -import { getCoreState, getPropsFromAttrs, toConnectedHTMLComponent } from '../utils/component-factory'; - -// ============================================================================ -// ROOT COMPONENT -// ============================================================================ - -type TimeSliderState = Prettify>; -type TimeSliderStateWithMethods = Prettify>; - -const timeSliderCreateRequestMethods = memoize(timeSliderStateDefinition.createRequestMethods); - -export const getTimeSliderRootState: StateHook = (element, mediaStore) => { - const mediaState = timeSliderStateDefinition.stateTransform(mediaStore.getState()); - const mediaMethods = timeSliderCreateRequestMethods(mediaStore.dispatch); - const coreState = getCoreState(CoreTimeSlider, { - ...getPropsFromAttrs(element), - ...mediaState, - ...mediaMethods, - }); - return { - ...coreState, - }; -}; - -/** - * TimeSlider Root props hook - equivalent to React's useTimeSliderRootProps - * Handles element attributes and properties based on state - */ -export const getTimeSliderRootProps: PropsHook = (element, state) => { - if (state._rootElement !== element) { - state._setRootElement(element); - } - - return { - role: 'slider', - tabindex: element.getAttribute('tabindex') ?? '0', - 'data-current-time': state.currentTime.toString(), - 'data-duration': state.duration.toString(), - 'data-orientation': element.orientation || 'horizontal', - 'aria-label': 'Seek', - 'aria-valuemin': '0', - 'aria-valuemax': Math.round(state.duration).toString(), - 'aria-valuenow': Math.round(state.currentTime).toString(), - 'aria-valuetext': `${state._currentTimeText} of ${state._durationText}`, - 'aria-orientation': element.orientation || 'horizontal', - style: { - ...(element.hasAttribute('commandfor') ? { anchorName: `--${element.getAttribute('commandfor')}` } : {}), - '--slider-fill': `${state._fillWidth.toFixed(3)}%`, - '--slider-pointer': `${(state._pointerWidth * 100).toFixed(3)}%`, - }, - }; -}; - -export class TimeSliderRoot extends HTMLElement { - static readonly observedAttributes: readonly string[] = ['commandfor', 'orientation']; - - _state: TimeSliderState | undefined; - - get orientation(): 'horizontal' | 'vertical' { - return (this.getAttribute('orientation') as 'horizontal' | 'vertical') || 'horizontal'; - } -} - -export const TimeSliderRootElement: ConnectedComponentConstructor = toConnectedHTMLComponent( - TimeSliderRoot, - getTimeSliderRootState, - getTimeSliderRootProps, - 'TimeSliderRoot', -); - -// ============================================================================ -// TRACK COMPONENT -// ============================================================================ - -export const getTimeSliderTrackProps: PropsHook = (element, _state) => { - const rootElement = element.closest('media-time-slider') as TimeSliderRoot; - - if (rootElement._state?._trackElement !== element) { - rootElement._state?._setTrackElement?.(element); - } - - const orientation = rootElement?.orientation || 'horizontal'; - - return { - 'data-orientation': orientation, - style: { - width: orientation === 'horizontal' ? '100%' : undefined, - height: orientation !== 'horizontal' ? '100%' : undefined, - }, - }; -}; - -export class TimeSliderTrack extends HTMLElement {} - -export const TimeSliderTrackElement: ConnectedComponentConstructor = toConnectedHTMLComponent( - TimeSliderTrack, - undefined, - getTimeSliderTrackProps, - 'TimeSliderTrack', -); - -// ============================================================================ -// PROGRESS COMPONENT -// ============================================================================ - -export const getTimeSliderProgressProps: PropsHook = (element, _state) => { - const rootElement = element.closest('media-time-slider') as TimeSliderRoot; - const orientation = rootElement?.orientation || 'horizontal'; - const style = orientation === 'horizontal' - ? { - position: 'absolute', - width: 'var(--slider-fill, 0%)', - height: '100%', - top: '0', - bottom: undefined, - } - : { - position: 'absolute', - height: 'var(--slider-fill, 0%)', - width: '100%', - bottom: '0', - top: undefined, - }; - - return { - 'data-orientation': orientation, - style, - }; -}; - -export class TimeSliderProgress extends HTMLElement {} - -export const TimeSliderProgressElement: ConnectedComponentConstructor = toConnectedHTMLComponent( - TimeSliderProgress, - undefined, - getTimeSliderProgressProps, - 'TimeSliderProgress', -); - -// ============================================================================ -// POINTER COMPONENT -// ============================================================================ - -export const getTimeSliderPointerProps: PropsHook = (element, _state) => { - const rootElement = element.closest('media-time-slider') as TimeSliderRoot; - const orientation = rootElement?.orientation || 'horizontal'; - const style = orientation === 'horizontal' - ? { - position: 'absolute', - width: 'var(--slider-pointer, 0%)', - height: '100%', - top: '0', - bottom: undefined, - } - : { - position: 'absolute', - height: 'var(--slider-pointer, 0%)', - width: '100%', - bottom: '0', - top: undefined, - }; - - return { - 'data-orientation': orientation, - style, - }; -}; - -export class TimeSliderPointer extends HTMLElement {} - -export const TimeSliderPointerElement: ConnectedComponentConstructor = toConnectedHTMLComponent( - TimeSliderPointer, - undefined, - getTimeSliderPointerProps, - 'TimeSliderPointer', -); - -// ============================================================================ -// THUMB COMPONENT -// ============================================================================ - -export const getTimeSliderThumbProps: PropsHook = (element, _state) => { - const rootElement = element.closest('media-time-slider') as TimeSliderRoot; - const orientation = rootElement?.orientation || 'horizontal'; - const style = orientation === 'horizontal' - ? { - position: 'absolute', - left: 'var(--slider-fill, 0%)', - top: '50%', - bottom: undefined, - translate: '-50% -50%', - } - : { - position: 'absolute', - bottom: 'var(--slider-fill, 0%)', - left: '50%', - top: undefined, - translate: '-50% 50%', - }; - - return { - 'data-orientation': orientation, - style, - }; -}; - -export class TimeSliderThumb extends HTMLElement {} - -export const TimeSliderThumbElement: ConnectedComponentConstructor = toConnectedHTMLComponent( - TimeSliderThumb, - undefined, - getTimeSliderThumbProps, - 'TimeSliderThumb', -); - -// ============================================================================ -// EXPORTS -// ============================================================================ - -export const TimeSliderElement = Object.assign( - {}, - { - Root: TimeSliderRootElement, - Track: TimeSliderTrackElement, - Progress: TimeSliderProgressElement, - Pointer: TimeSliderPointerElement, - Thumb: TimeSliderThumbElement, - }, -) as { - Root: typeof TimeSliderRootElement; - Track: typeof TimeSliderTrackElement; - Progress: typeof TimeSliderProgressElement; - Pointer: typeof TimeSliderPointerElement; - Thumb: typeof TimeSliderThumbElement; -}; diff --git a/packages/__tech-preview__/html/src/elements/tooltip.ts b/packages/__tech-preview__/html/src/elements/tooltip.ts deleted file mode 100644 index 607966ee..00000000 --- a/packages/__tech-preview__/html/src/elements/tooltip.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { Prettify } from '../types'; -import type { ConnectedComponentConstructor, PropsHook, StateHook } from '../utils/component-factory'; -import { Tooltip as CoreTooltip } from '@videojs/core-preview'; -import { getCoreState, getPropsFromAttrs, toConnectedHTMLComponent } from '../utils/component-factory'; -import { getPopoverProps, Popover } from './popover'; - -export type TooltipState = Prettify>; - -export const getTooltipState: StateHook = (element, _mediaStore) => { - const coreState = getCoreState(CoreTooltip, getPropsFromAttrs(element)); - return { - ...coreState, - }; -}; - -export const getTooltipProps: PropsHook = getPopoverProps; - -export class Tooltip extends Popover { - static get observedAttributes(): string[] { - return [ - ...super.observedAttributes, - 'track-cursor-axis', - ]; - } - - get trackCursorAxis(): 'x' | null { - const value = this.getAttribute('track-cursor-axis'); - return value === 'x' ? value : null; - } -} - -export const TooltipElement: ConnectedComponentConstructor = toConnectedHTMLComponent( - Tooltip, - getTooltipState, - getTooltipProps, - 'Tooltip', -); diff --git a/packages/__tech-preview__/html/src/elements/volume-slider.ts b/packages/__tech-preview__/html/src/elements/volume-slider.ts deleted file mode 100644 index fd8bcbfa..00000000 --- a/packages/__tech-preview__/html/src/elements/volume-slider.ts +++ /dev/null @@ -1,203 +0,0 @@ -import type { Prettify } from '../types'; -import type { ConnectedComponentConstructor, PropsHook, StateHook } from '../utils/component-factory'; - -import { VolumeSlider as CoreVolumeSlider } from '@videojs/core-preview'; -import { volumeSliderStateDefinition } from '@videojs/core-preview/store'; -import { memoize } from '@videojs/utils-preview'; - -import { getCoreState, getPropsFromAttrs, toConnectedHTMLComponent } from '../utils/component-factory'; - -// ============================================================================ -// ROOT COMPONENT -// ============================================================================ - -type VolumeSliderState = Prettify>; -type VolumeSliderStateWithMethods = Prettify>; - -const volumeSliderCreateRequestMethods = memoize(volumeSliderStateDefinition.createRequestMethods); - -export const getVolumeSliderRootState: StateHook = (element, mediaStore) => { - const mediaState = volumeSliderStateDefinition.stateTransform(mediaStore.getState()); - const mediaMethods = volumeSliderCreateRequestMethods(mediaStore.dispatch); - const coreState = getCoreState(CoreVolumeSlider, { - ...getPropsFromAttrs(element), - ...mediaState, - ...mediaMethods, - }); - return { - ...coreState, - }; -}; - -/** - * VolumeSlider Root props hook - equivalent to React's useVolumeSliderRootProps - * Handles element attributes and properties based on state - */ -export const getVolumeSliderRootProps: PropsHook = (element, state) => { - if (state._rootElement !== element) { - state._setRootElement(element); - } - - const volumeText = `${Math.round(state.muted ? 0 : state.volume * 100)}%`; - - return { - role: 'slider', - tabindex: element.getAttribute('tabindex') ?? '0', - 'data-muted': state.muted.toString(), - 'data-volume-level': state.volumeLevel, - 'data-orientation': element.orientation || 'horizontal', - 'aria-label': 'Volume', - 'aria-valuemin': '0', - 'aria-valuemax': '100', - 'aria-valuenow': Math.round(state.muted ? 0 : state.volume * 100).toString(), - 'aria-valuetext': volumeText, - 'aria-orientation': element.orientation || 'horizontal', - style: { - ...(element.hasAttribute('commandfor') ? { anchorName: `--${element.getAttribute('commandfor')}` } : {}), - '--slider-fill': `${state._fillWidth.toFixed(3)}%`, - }, - }; -}; - -export class VolumeSliderRoot extends HTMLElement { - static readonly observedAttributes: readonly string[] = ['commandfor', 'orientation']; - - _state: VolumeSliderState | undefined; - - get orientation(): 'horizontal' | 'vertical' { - return (this.getAttribute('orientation') as 'horizontal' | 'vertical') || 'horizontal'; - } -} - -export const VolumeSliderRootElement: ConnectedComponentConstructor = toConnectedHTMLComponent( - VolumeSliderRoot, - getVolumeSliderRootState, - getVolumeSliderRootProps, - 'VolumeSliderRoot', -); - -// ============================================================================ -// TRACK COMPONENT -// ============================================================================ - -export const getVolumeSliderTrackProps: PropsHook = (element, _state) => { - const rootElement = element.closest('media-volume-slider') as VolumeSliderRoot; - - if (rootElement._state?._trackElement !== element) { - rootElement._state?._setTrackElement?.(element); - } - - const orientation = rootElement?.orientation || 'horizontal'; - - return { - 'data-orientation': orientation, - style: { - width: orientation === 'horizontal' ? '100%' : undefined, - height: orientation !== 'horizontal' ? '100%' : undefined, - }, - }; -}; - -export class VolumeSliderTrack extends HTMLElement {} - -export const VolumeSliderTrackElement: ConnectedComponentConstructor = toConnectedHTMLComponent( - VolumeSliderTrack, - undefined, - getVolumeSliderTrackProps, - 'VolumeSliderTrack', -); - -// ============================================================================ -// INDICATOR COMPONENT -// ============================================================================ - -export const getVolumeSliderIndicatorProps: PropsHook = (element, _state) => { - const rootElement = element.closest('media-volume-slider') as VolumeSliderRoot; - const orientation = rootElement?.orientation || 'horizontal'; - const style = orientation === 'horizontal' - ? { - position: 'absolute', - width: 'var(--slider-fill, 0%)', - height: '100%', - top: '0', - bottom: undefined, - } - : { - position: 'absolute', - height: 'var(--slider-fill, 0%)', - width: '100%', - bottom: '0', - top: undefined, - }; - - return { - 'data-orientation': orientation, - style, - }; -}; - -export class VolumeSliderIndicator extends HTMLElement {} - -export const VolumeSliderIndicatorElement: ConnectedComponentConstructor = toConnectedHTMLComponent( - VolumeSliderIndicator, - undefined, - getVolumeSliderIndicatorProps, - 'VolumeSliderIndicator', -); - -// ============================================================================ -// THUMB COMPONENT -// ============================================================================ - -export const getVolumeSliderThumbProps: PropsHook = (element, _state) => { - const rootElement = element.closest('media-volume-slider') as VolumeSliderRoot; - const orientation = rootElement?.orientation || 'horizontal'; - const style = orientation === 'horizontal' - ? { - position: 'absolute', - left: 'var(--slider-fill, 0%)', - top: '50%', - bottom: undefined, - translate: '-50% -50%', - } - : { - position: 'absolute', - bottom: 'var(--slider-fill, 0%)', - left: '50%', - top: undefined, - translate: '-50% 50%', - }; - - return { - 'data-orientation': orientation, - style, - }; -}; - -export class VolumeSliderThumb extends HTMLElement {} - -export const VolumeSliderThumbElement: ConnectedComponentConstructor = toConnectedHTMLComponent( - VolumeSliderThumb, - undefined, - getVolumeSliderThumbProps, - 'VolumeSliderThumb', -); - -// ============================================================================ -// EXPORTS -// ============================================================================ - -export const VolumeSliderElement = Object.assign( - {}, - { - Root: VolumeSliderRootElement, - Track: VolumeSliderTrackElement, - Indicator: VolumeSliderIndicatorElement, - Thumb: VolumeSliderThumbElement, - }, -) as { - Root: typeof VolumeSliderRootElement; - Track: typeof VolumeSliderTrackElement; - Indicator: typeof VolumeSliderIndicatorElement; - Thumb: typeof VolumeSliderThumbElement; -}; diff --git a/packages/__tech-preview__/html/src/icons/index.ts b/packages/__tech-preview__/html/src/icons/index.ts deleted file mode 100644 index 389c891c..00000000 --- a/packages/__tech-preview__/html/src/icons/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import './media-fullscreen-enter-alt-icon'; -import './media-fullscreen-enter-icon'; -import './media-fullscreen-exit-alt-icon'; -import './media-fullscreen-exit-icon'; -import './media-pause-icon'; -import './media-play-icon'; -import './media-volume-high-icon'; -import './media-volume-low-icon'; -import './media-volume-off-icon'; diff --git a/packages/__tech-preview__/html/src/icons/media-chrome-icon.ts b/packages/__tech-preview__/html/src/icons/media-chrome-icon.ts deleted file mode 100644 index f9a5c284..00000000 --- a/packages/__tech-preview__/html/src/icons/media-chrome-icon.ts +++ /dev/null @@ -1,26 +0,0 @@ -export function getTemplateHTML() { - return /* html */ ` - - `; -} - -export class MediaChromeIcon extends HTMLElement { - static shadowRootOptions = { mode: 'open' as ShadowRootMode }; - static getTemplateHTML: () => string = getTemplateHTML; - - constructor() { - super(); - - if (!this.shadowRoot) { - this.attachShadow((this.constructor as typeof MediaChromeIcon).shadowRootOptions); - this.shadowRoot!.innerHTML = (this.constructor as typeof MediaChromeIcon).getTemplateHTML(); - } - } -} diff --git a/packages/__tech-preview__/html/src/icons/media-fullscreen-enter-alt-icon.ts b/packages/__tech-preview__/html/src/icons/media-fullscreen-enter-alt-icon.ts deleted file mode 100644 index 30942a2e..00000000 --- a/packages/__tech-preview__/html/src/icons/media-fullscreen-enter-alt-icon.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { SVG_ICONS } from '@videojs/icons-preview'; - -import { defineCustomElement } from '@/utils/custom-element'; -import { MediaChromeIcon } from './media-chrome-icon'; - -export function getTemplateHTML() { - return /* html */ ` - ${MediaChromeIcon.getTemplateHTML()} - - ${SVG_ICONS.fullscreenEnterAlt} - `; -} - -export class MediaFullscreenEnterAltIconElement extends MediaChromeIcon { - static getTemplateHTML: () => string = getTemplateHTML; -} - -defineCustomElement('media-fullscreen-enter-alt-icon', MediaFullscreenEnterAltIconElement); diff --git a/packages/__tech-preview__/html/src/icons/media-fullscreen-enter-icon.ts b/packages/__tech-preview__/html/src/icons/media-fullscreen-enter-icon.ts deleted file mode 100644 index 55e3672d..00000000 --- a/packages/__tech-preview__/html/src/icons/media-fullscreen-enter-icon.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { SVG_ICONS } from '@videojs/icons-preview'; - -import { defineCustomElement } from '@/utils/custom-element'; -import { MediaChromeIcon } from './media-chrome-icon'; - -export function getTemplateHTML() { - return /* html */ ` - ${MediaChromeIcon.getTemplateHTML()} - - ${SVG_ICONS.fullscreenEnter} - `; -} - -export class MediaFullscreenEnterIconElement extends MediaChromeIcon { - static getTemplateHTML: () => string = getTemplateHTML; -} - -defineCustomElement('media-fullscreen-enter-icon', MediaFullscreenEnterIconElement); diff --git a/packages/__tech-preview__/html/src/icons/media-fullscreen-exit-alt-icon.ts b/packages/__tech-preview__/html/src/icons/media-fullscreen-exit-alt-icon.ts deleted file mode 100644 index 5ee266ad..00000000 --- a/packages/__tech-preview__/html/src/icons/media-fullscreen-exit-alt-icon.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { SVG_ICONS } from '@videojs/icons-preview'; - -import { defineCustomElement } from '@/utils/custom-element'; -import { MediaChromeIcon } from './media-chrome-icon'; - -export function getTemplateHTML() { - return /* html */ ` - ${MediaChromeIcon.getTemplateHTML()} - - ${SVG_ICONS.fullscreenExitAlt} - `; -} - -export class MediaFullscreenExitAltIconElement extends MediaChromeIcon { - static getTemplateHTML: () => string = getTemplateHTML; -} - -defineCustomElement('media-fullscreen-exit-alt-icon', MediaFullscreenExitAltIconElement); diff --git a/packages/__tech-preview__/html/src/icons/media-fullscreen-exit-icon.ts b/packages/__tech-preview__/html/src/icons/media-fullscreen-exit-icon.ts deleted file mode 100644 index 42983dd4..00000000 --- a/packages/__tech-preview__/html/src/icons/media-fullscreen-exit-icon.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { SVG_ICONS } from '@videojs/icons-preview'; - -import { defineCustomElement } from '@/utils/custom-element'; -import { MediaChromeIcon } from './media-chrome-icon'; - -export function getTemplateHTML() { - return /* html */ ` - ${MediaChromeIcon.getTemplateHTML()} - - ${SVG_ICONS.fullscreenExit} - `; -} - -export class MediaFullscreenExitIconElement extends MediaChromeIcon { - static getTemplateHTML: () => string = getTemplateHTML; -} - -defineCustomElement('media-fullscreen-exit-icon', MediaFullscreenExitIconElement); diff --git a/packages/__tech-preview__/html/src/icons/media-pause-icon.ts b/packages/__tech-preview__/html/src/icons/media-pause-icon.ts deleted file mode 100644 index a7c99ef5..00000000 --- a/packages/__tech-preview__/html/src/icons/media-pause-icon.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { SVG_ICONS } from '@videojs/icons-preview'; - -import { defineCustomElement } from '@/utils/custom-element'; -import { MediaChromeIcon } from './media-chrome-icon'; - -export function getTemplateHTML() { - return /* html */ ` - ${MediaChromeIcon.getTemplateHTML()} - ${SVG_ICONS.pause} - `; -} - -export class MediaPauseIconElement extends MediaChromeIcon { - static getTemplateHTML: () => string = getTemplateHTML; -} - -defineCustomElement('media-pause-icon', MediaPauseIconElement); diff --git a/packages/__tech-preview__/html/src/icons/media-play-icon.ts b/packages/__tech-preview__/html/src/icons/media-play-icon.ts deleted file mode 100644 index 2f6a7e68..00000000 --- a/packages/__tech-preview__/html/src/icons/media-play-icon.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { SVG_ICONS } from '@videojs/icons-preview'; - -import { defineCustomElement } from '@/utils/custom-element'; -import { MediaChromeIcon } from './media-chrome-icon'; - -export function getTemplateHTML() { - return /* html */ ` - ${MediaChromeIcon.getTemplateHTML()} - - ${SVG_ICONS.play} - `; -} - -export class MediaPlayIconElement extends MediaChromeIcon { - static getTemplateHTML: () => string = getTemplateHTML; -} - -defineCustomElement('media-play-icon', MediaPlayIconElement); diff --git a/packages/__tech-preview__/html/src/icons/media-volume-high-icon.ts b/packages/__tech-preview__/html/src/icons/media-volume-high-icon.ts deleted file mode 100644 index 562c47d5..00000000 --- a/packages/__tech-preview__/html/src/icons/media-volume-high-icon.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { SVG_ICONS } from '@videojs/icons-preview'; - -import { defineCustomElement } from '@/utils/custom-element'; -import { MediaChromeIcon } from './media-chrome-icon'; - -export function getTemplateHTML() { - return /* html */ ` - ${MediaChromeIcon.getTemplateHTML()} - - ${SVG_ICONS.volumeHigh} - `; -} - -export class MediaVolumeHighIconElement extends MediaChromeIcon { - static getTemplateHTML: () => string = getTemplateHTML; -} - -defineCustomElement('media-volume-high-icon', MediaVolumeHighIconElement); diff --git a/packages/__tech-preview__/html/src/icons/media-volume-low-icon.ts b/packages/__tech-preview__/html/src/icons/media-volume-low-icon.ts deleted file mode 100644 index 0c1bd598..00000000 --- a/packages/__tech-preview__/html/src/icons/media-volume-low-icon.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { SVG_ICONS } from '@videojs/icons-preview'; - -import { defineCustomElement } from '@/utils/custom-element'; -import { MediaChromeIcon } from './media-chrome-icon'; - -export function getTemplateHTML() { - return /* html */ ` - ${MediaChromeIcon.getTemplateHTML()} - - ${SVG_ICONS.volumeLow} - `; -} - -export class MediaVolumeLowIconElement extends MediaChromeIcon { - static getTemplateHTML: () => string = getTemplateHTML; -} - -defineCustomElement('media-volume-low-icon', MediaVolumeLowIconElement); diff --git a/packages/__tech-preview__/html/src/icons/media-volume-off-icon.ts b/packages/__tech-preview__/html/src/icons/media-volume-off-icon.ts deleted file mode 100644 index 05d74b10..00000000 --- a/packages/__tech-preview__/html/src/icons/media-volume-off-icon.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { SVG_ICONS } from '@videojs/icons-preview'; - -import { defineCustomElement } from '@/utils/custom-element'; -import { MediaChromeIcon } from './media-chrome-icon'; - -export function getTemplateHTML() { - return /* html */ ` - ${MediaChromeIcon.getTemplateHTML()} - - ${SVG_ICONS.volumeOff} - `; -} - -export class MediaVolumeOffIconElement extends MediaChromeIcon { - static getTemplateHTML: () => string = getTemplateHTML; -} - -defineCustomElement('media-volume-off-icon', MediaVolumeOffIconElement); diff --git a/packages/__tech-preview__/html/src/index.ts b/packages/__tech-preview__/html/src/index.ts deleted file mode 100644 index 672d5b17..00000000 --- a/packages/__tech-preview__/html/src/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -export { CurrentTimeDisplayElement } from './elements/current-time-display'; -export { DurationDisplayElement } from './elements/duration-display'; -export { FullscreenButtonElement } from './elements/fullscreen-button'; -export { MuteButtonElement } from './elements/mute-button'; -export { PlayButtonElement } from './elements/play-button'; -export { PopoverElement } from './elements/popover'; -export { PreviewTimeDisplayElement } from './elements/preview-time-display'; -export { TimeSliderElement } from './elements/time-slider'; -export { TooltipElement } from './elements/tooltip'; -export { VolumeSliderElement } from './elements/volume-slider'; -export { MediaContainerElement } from './media/media-container'; -export { MediaSkinElement } from './media/media-skin'; -export { VideoProviderElement } from './media/video-provider'; diff --git a/packages/__tech-preview__/html/src/media/media-container.ts b/packages/__tech-preview__/html/src/media/media-container.ts deleted file mode 100644 index df3780af..00000000 --- a/packages/__tech-preview__/html/src/media/media-container.ts +++ /dev/null @@ -1,234 +0,0 @@ -import type { Constructor, CustomElement } from '@open-wc/context-protocol'; - -import { ConsumerMixin } from '@open-wc/context-protocol'; - -export function getTemplateHTML() { - return /* html */ ` - - - `; -} - -export const Attributes = { - // Inputs - AUTOHIDE: 'autohide', - AUTOHIDE_OVER_CONTROLS: 'autohideovercontrols', - // Output - CONTROLS_VISIBILITY: 'data-controls', -} as const; - -const DEFAULT_AUTOHIDE = 2000; // 2 seconds - -const CustomElementConsumer: Constructor = ConsumerMixin(HTMLElement); - -export class MediaContainerElement extends CustomElementConsumer { - static shadowRootOptions = { mode: 'open' as ShadowRootMode }; - static getTemplateHTML: () => string = getTemplateHTML; - - static get observedAttributes(): string[] { - return [Attributes.AUTOHIDE]; - } - - _mediaStore: any; - _mediaSlot: HTMLSlotElement; - _paused: boolean = true; - contexts = { - mediaStore: (mediaStore: any): void => { - this._mediaStore = mediaStore; - this._handleMediaSlotChange(); - this._registerContainerStateOwner(); - this._subscribeToPlayState(); - }, - }; - - _pointerDownTimeStamp = 0; - _inactiveTimeout: ReturnType | undefined = undefined; - _autohide: number = DEFAULT_AUTOHIDE; - _isUserActive: boolean = true; - - constructor() { - super(); - - if (!this.shadowRoot) { - this.attachShadow((this.constructor as typeof MediaContainerElement).shadowRootOptions); - this.shadowRoot!.innerHTML = (this.constructor as typeof MediaContainerElement).getTemplateHTML(); - } - - this._mediaSlot = this.shadowRoot!.querySelector('slot[name=media]') as HTMLSlotElement; - this._mediaSlot.addEventListener('slotchange', this._handleMediaSlotChange); - - // Add click handler for play/pause functionality - this.addEventListener('click', this._handleClick); - - // Add handlers for user activity detection - this.addEventListener('pointerdown', this._handlePointerDown); - this.addEventListener('pointermove', this._handlePointerMove); - this.addEventListener('pointerup', this._handlePointerUp); - this.addEventListener('mouseleave', this._setUserInactive); - this.addEventListener('keyup', this._scheduleUserInactive); - } - - connectedCallback(): void { - super.connectedCallback?.(); - this._registerContainerStateOwner(); - } - - disconnectedCallback(): void { - super.disconnectedCallback?.(); - this._unregisterContainerStateOwner(); - } - - attributeChangedCallback( - attrName: string, - _oldValue: string, - newValue: string, - ): void { - if (attrName.toLowerCase() === Attributes.AUTOHIDE) { - this.autohide = newValue; - } - } - - _registerContainerStateOwner = (): void => { - if (!this._mediaStore) return; - this._mediaStore.dispatch({ type: 'containerstateownerchangerequest', detail: this }); - }; - - _unregisterContainerStateOwner = (): void => { - if (!this._mediaStore) return; - this._mediaStore.dispatch({ type: 'containerstateownerchangerequest', detail: null }); - }; - - _getMediaElement = (): HTMLMediaElement | null => { - const media = this._mediaSlot.assignedElements({ flatten: true })[0]; - if (media && (media instanceof HTMLMediaElement)) { - return media; - } - return null; - }; - - _handleMediaSlotChange = (): void => { - const media = this._getMediaElement(); - this._mediaStore.dispatch({ type: 'mediastateownerchangerequest', detail: media }); - }; - - _handleClick = (event: PointerEvent): void => { - if (!this._mediaStore) return; - // Ignore clicks from touch/pen devices - if (navigator.maxTouchPoints > 0 || event.pointerType !== 'mouse') return; - // Ignore clicks not on media elements - if (!['video', 'audio'].includes((event.target as HTMLElement).localName || '')) return; - - if (this._paused) { - this._mediaStore.dispatch({ type: 'playrequest' }); - } else { - this._mediaStore.dispatch({ type: 'pauserequest' }); - } - }; - - _handlePointerDown = (event: PointerEvent): void => { - this._pointerDownTimeStamp = event.timeStamp; - }; - - _subscribeToPlayState = (): void => { - if (!this._mediaStore) return; - - // Subscribe to paused state changes - this._mediaStore.subscribe((state: any) => { - this._paused = state.paused ?? true; - this._updateControlsVisibility(); - }); - }; - - _handlePointerMove = (event: PointerEvent): void => { - if (event.pointerType !== 'mouse') { - // On mobile we toggle the controls on a tap which is handled in pointerup, - // but Android fires pointermove events even when the user is just tapping. - // Prevent calling setActive() on tap because it will mess with the toggle logic. - const MAX_TAP_DURATION = 250; - // If the move duration exceeds 250ms then it's a drag and we should show the controls. - if (event.timeStamp - this._pointerDownTimeStamp < MAX_TAP_DURATION) return; - } - - this._setUserActive(); - - // Stay visible if hovered over control bar - clearTimeout(this._inactiveTimeout); - - const media = this._getMediaElement(); - - // If hovering over something other than controls, we're free to make inactive - const autohideOverControls = this.hasAttribute(Attributes.AUTOHIDE_OVER_CONTROLS); - if ([this, media].includes(event.target as HTMLMediaElement) || autohideOverControls) { - this._scheduleUserInactive(); - } - }; - - _handlePointerUp = (event: PointerEvent): void => { - if (navigator.maxTouchPoints > 0 || event.pointerType !== 'mouse') { - const media = this._getMediaElement(); - - // Toggle behavior: if user is active, make inactive; otherwise make active - if ( - [this, media].includes(event.target as HTMLMediaElement) - && this._isUserActive - ) { - this._setUserInactive(); - } else { - this._scheduleUserInactive(); - } - } else if ( - event - .composedPath() - .some(element => - element instanceof HTMLElement && ['media-play-button', 'media-fullscreen-button'].includes( - element?.localName, - ), - ) - ) { - this._scheduleUserInactive(); - } - }; - - _updateControlsVisibility = (): void => { - // Match React logic: visible if user active OR paused - const shouldBeVisible = this._isUserActive || this._paused; - const targetValue = shouldBeVisible ? 'visible' : 'hidden'; - - if (this.getAttribute(Attributes.CONTROLS_VISIBILITY) !== targetValue) { - this.setAttribute(Attributes.CONTROLS_VISIBILITY, targetValue); - } - }; - - _setUserActive = (): void => { - this._isUserActive = true; - this._updateControlsVisibility(); - }; - - _setUserInactive = (): void => { - if (typeof this._autohide !== 'number' || this._autohide < 0) return; - this._isUserActive = false; - this._updateControlsVisibility(); - }; - - _scheduleUserInactive = (): void => { - this._setUserActive(); - clearTimeout(this._inactiveTimeout); - const autohide = Number.parseInt(this.autohide); - - // Setting autohide to -1 turns off autohide - if (autohide < 0) return; - - this._inactiveTimeout = setTimeout(() => { - this._setUserInactive(); - }, autohide); - }; - - set autohide(ms: string) { - const parsed = Number(ms); - this._autohide = Number.isNaN(parsed) ? 0 : parsed; - } - - get autohide(): string { - return (this._autohide === undefined ? DEFAULT_AUTOHIDE : this._autohide).toString(); - } -} diff --git a/packages/__tech-preview__/html/src/media/media-skin.ts b/packages/__tech-preview__/html/src/media/media-skin.ts deleted file mode 100644 index f1b1b317..00000000 --- a/packages/__tech-preview__/html/src/media/media-skin.ts +++ /dev/null @@ -1,30 +0,0 @@ -export function getTemplateHTML() { - return /* html */ ` - - - `; -} - -export class MediaSkinElement extends HTMLElement { - static shadowRootOptions = { mode: 'open' as ShadowRootMode }; - static getTemplateHTML: () => string = getTemplateHTML; - - constructor() { - super(); - - if (!this.shadowRoot) { - this.attachShadow((this.constructor as typeof MediaSkinElement).shadowRootOptions); - this.shadowRoot!.innerHTML = (this.constructor as typeof MediaSkinElement).getTemplateHTML(); - } - } -} diff --git a/packages/__tech-preview__/html/src/media/video-provider.ts b/packages/__tech-preview__/html/src/media/video-provider.ts deleted file mode 100644 index 6a898b3e..00000000 --- a/packages/__tech-preview__/html/src/media/video-provider.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { Constructor, CustomElement } from '@open-wc/context-protocol'; -import type { MediaStore } from '@videojs/core-preview/store'; - -import { ProviderMixin } from '@open-wc/context-protocol'; -import { createMediaStore } from '@videojs/core-preview/store'; - -import { printConsoleBanner } from '@videojs/utils-preview'; -import { version } from '../../package.json'; - -printConsoleBanner(version); - -const ProviderHTMLElement: Constructor = ProviderMixin(HTMLElement); - -export class VideoProviderElement extends ProviderHTMLElement { - contexts = { - mediaStore: (): MediaStore => { - return createMediaStore(); - }, - }; -} diff --git a/packages/__tech-preview__/html/src/skins/frosted/index.ts b/packages/__tech-preview__/html/src/skins/frosted/index.ts deleted file mode 100644 index 0ccf2160..00000000 --- a/packages/__tech-preview__/html/src/skins/frosted/index.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { MediaSkinElement } from '@/media/media-skin'; -import { defineCustomElement } from '@/utils/custom-element'; -import styles from './styles.css'; -// be sure to import video-provider first for proper context initialization -import '@/define/video-provider'; -import '@/define/media-container'; -import '@/define/media-play-button'; -import '@/define/media-mute-button'; -import '@/define/media-volume-slider'; -import '@/define/media-time-slider'; -import '@/define/media-fullscreen-button'; -import '@/define/media-duration-display'; -import '@/define/media-current-time-display'; -import '@/define/media-preview-time-display'; -import '@/define/media-popover'; -import '@/define/media-tooltip'; -import '@/icons'; - -export function getTemplateHTML() { - return /* html */` - ${MediaSkinElement.getTemplateHTML()} - - - - - -
- -
- - - - - - - - Play - Pause - - -
- - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - Enter Fullscreen - Exit Fullscreen - -
-
- `; -} - -export class MediaSkinFrostedElement extends MediaSkinElement { - static getTemplateHTML: () => string = getTemplateHTML; -} - -defineCustomElement('media-skin-frosted', MediaSkinFrostedElement); diff --git a/packages/__tech-preview__/html/src/skins/frosted/styles.css b/packages/__tech-preview__/html/src/skins/frosted/styles.css deleted file mode 100644 index 1429a77e..00000000 --- a/packages/__tech-preview__/html/src/skins/frosted/styles.css +++ /dev/null @@ -1,354 +0,0 @@ -media-container * { - box-sizing: border-box; -} - -media-container { - position: relative; - isolation: isolate; - container: root / inline-size; - overflow: clip; - border-radius: var(--frosted-border-radius, 2rem); - background: oklab(0 0 0); - font-family: ui-sans-serif, system-ui, sans-serif; - font-size: 0.8125rem; - line-height: 1.5; - -webkit-font-smoothing: auto; - -moz-osx-font-smoothing: auto; -} -media-container::before, -media-container::after { - content: ''; - position: absolute; - pointer-events: none; - border-radius: inherit; - z-index: 10; -} -media-container::before { - inset: 1px; - box-shadow: inset 0 0 0 1px oklab(1 0 0 / 0.15); -} -media-container::after { - inset: 0; - box-shadow: inset 0 0 0 1px oklab(0 0 0 / 0.1); -} - -/* Fullscreen */ -media-container:fullscreen { - border-radius: 0; -} - -media-container > ::slotted([slot='media']) { - display: block; - width: 100%; - height: 100%; -} - -/* Media Container UI Overlay Styling */ -media-container > .overlay { - position: absolute; - inset: 0; - display: flex; - flex-flow: column nowrap; - align-items: start; - pointer-events: none; - border-radius: inherit; - background-image: linear-gradient(to top, oklab(0 0 0 / 0.5), oklab(0 0 0 / 0.3), rgba(0, 0, 0, 0)); - backdrop-filter: saturate(1.5) brightness(0.9); - transition-property: opacity; - transition-duration: 100ms; - transition-delay: 0ms; - transition-timing-function: ease-out; -} -media-container[data-controls='hidden'] > .overlay { - transition-delay: 500ms; - transition-duration: 300ms; - opacity: 0; -} - -/* Common Surface Styles - e.g. tooltips, popovers, controls */ -.surface { - background-color: oklab(1 0 0 / 0.1); - backdrop-filter: blur(64px) brightness(0.9) saturate(1.5); - box-shadow: - inset 0 0 0 1px oklab(1 0 0 / 0.05), - 0 0 0 1px oklab(0 0 0 / 0.15), - oklab(0 0 0 / 0.15) 0px 1px 3px 0px, - oklab(0 0 0 / 0.15) 0px 1px 2px -1px; -} -@media (prefers-reduced-transparency: reduce) { - .surface { - background-color: oklab(0 0 0 / 0.7); - box-shadow: - inset 0 0 0 1px oklab(0 0 0), - 0 0 0 1px oklab(1 0 0 / 0.2); - } -} -@media (prefers-contrast: more) { - .surface { - background-color: oklab(0 0 0 / 0.9); - box-shadow: - inset 0 0 0 1px oklab(0 0 0), - 0 0 0 1px oklab(1 0 0 / 0.2); - } -} - -/* Media Control Bar UI/Styles */ -.control-bar { - position: absolute; - bottom: 0.75rem; - inset-inline: 0.75rem; - padding: 0.25rem; - display: flex; - align-items: center; - gap: 0.125rem; - border-radius: calc(infinity * 1px); - will-change: scale, transform, filter, opacity; - transition-property: scale, transform, filter, opacity; - transition-delay: 0ms; - transition-duration: 100ms; - transition-timing-function: ease-out; - transform-origin: bottom; - color: oklab(1 0 0); -} -media-container[data-controls='hidden'] > .control-bar { - opacity: 0; - scale: 0.9; - filter: blur(8px); - transition-delay: 500ms; - transition-duration: 300ms; - pointer-events: none; -} - -/* Time Display Styling */ -.time-controls { - display: flex; - align-items: center; - flex: 1; - gap: 0.75rem; - padding-inline: 0.375rem; -} -media-current-time-display, -media-duration-display { - text-shadow: 0 1px 0 oklab(0 0 0 / 0.25); - font-variant-numeric: tabular-nums; -} - -/* Generic Media Button Styling */ -.button { - display: grid; - flex-shrink: 0; - padding: 0.5rem; - cursor: pointer; - background: transparent; - border: none; - border-radius: calc(infinity * 1px); - color: oklab(1 0 0 / 0.9); - user-select: none; - outline: 2px solid transparent; - outline-offset: -2px; - transition-property: background-color, color, outline-offset; - transition-duration: 150ms; - transition-timing-function: ease-out; -} -.button:hover, -.button:focus-visible, -.button[aria-expanded='true'] { - background-color: oklab(1 0 0 / 0.1); - color: oklab(1 0 0); - text-decoration: none; -} -.button:focus-visible { - outline-color: oklch(62.3% 0.214 259.815); - outline-offset: 2px; -} -.button[disabled] { - cursor: not-allowed; - opacity: 0.5; - filter: grayscale(1); -} - -.button .icon { - grid-area: 1 / 1; - width: 18px; - height: 18px; - filter: drop-shadow(0 1px 0 oklab(0 0 0 / 0.25)); -} - -/* Media Play Button UI/Styles */ -media-play-button .icon { - opacity: 0; - transition: opacity 150ms linear; -} - -media-play-button:not([data-paused]) .pause-icon, -media-play-button[data-paused] .play-icon { - opacity: 1; -} - -/* Media Fullscreen Button UI/Styles */ -media-fullscreen-button .icon { - display: none; -} -media-fullscreen-button:not([data-fullscreen]) .fullscreen-enter-icon, -media-fullscreen-button[data-fullscreen] .fullscreen-exit-icon { - display: inline; -} - -/* One way to define the "default visible" icon (CJP) */ -media-mute-button .icon { - display: none; -} -media-mute-button:not([data-volume-level]) .volume-low-icon, -media-mute-button[data-volume-level='high'] .volume-high-icon, -media-mute-button[data-volume-level='low'] .volume-low-icon, -media-mute-button[data-volume-level='medium'] .volume-low-icon, -media-mute-button[data-volume-level='off'] .volume-off-icon { - display: inline; -} - -/* TimeSlider Component Styles */ -.slider { - flex: 1; - display: flex; - align-items: center; - justify-content: center; - position: relative; - border-radius: calc(infinity * 1px); - outline: none; -} - -/* Horizontal orientation styles */ -.slider[data-orientation='horizontal'] { - min-width: 5rem; - width: 100%; - height: 1.25rem; -} - -/* Vertical orientation styles */ -.slider[data-orientation='vertical'] { - height: 5rem; - width: 1.25rem; -} - -.slider-track { - position: relative; - isolation: isolate; - background-color: oklab(1 0 0 / 0.2); - border-radius: inherit; - overflow: hidden; - user-select: none; - outline: 2px solid transparent; - outline-offset: -2px; - transition: outline-offset 150ms ease-out; - box-shadow: 0 0 0 1px oklab(0 0 0 / 0.05); -} - -/* Horizontal track styles */ -.slider-track[data-orientation='horizontal'] { - width: 100%; - height: 0.25rem; -} - -/* Vertical track styles */ -.slider-track[data-orientation='vertical'] { - width: 0.25rem; - height: 100%; -} - -.slider:focus-visible .slider-track { - outline-color: oklch(62.3% 0.214 259.815); - outline-offset: 6px; -} - -.slider-thumb { - width: 0.625rem; - height: 0.625rem; - background-color: oklab(1 0 0); - border-radius: calc(infinity * 1px); - user-select: none; - z-index: 10; - box-shadow: - 0 0 0 1px oklab(0 0 0 / 0.1), - 0 1px 3px 0 oklab(0 0 0 / 0.15), - 0 1px 2px -1px oklab(0 0 0 / 0.15); - opacity: 0; - transition-property: opacity, height, width; - transition-duration: 150ms; - transition-timing-function: ease-out; -} -.slider-thumb:active { - width: 0.75rem; - height: 0.75rem; -} -.slider:hover .slider-thumb, -.slider:focus-within .slider-thumb { - opacity: 1; -} -.slider-track[data-orientation='horizontal'] .slider-thumb { - cursor: ew-resize; -} -.slider-track[data-orientation='vertical'] .slider-thumb { - cursor: ns-resize; -} - -.slider-pointer { - background-color: oklab(1 0 0 / 0.2); - border-radius: inherit; -} - -.slider-progress { - background-color: oklab(1 0 0); - border-radius: inherit; -} - -media-preview-time-display { - font-variant-numeric: tabular-nums; -} - -.popup-animation { - transition-property: transform, scale, opacity, filter; - transition-duration: 200ms; - transform: scale(1); - transform-origin: bottom; - opacity: 1; - filter: blur(0px); -} -.popup-animation[data-starting-style], -.popup-animation[data-ending-style] { - transform: scale(0); - opacity: 0; - filter: blur(8px); -} -.popup-animation[data-instant] { - transition-duration: 0ms; -} - -media-popover { - padding: 0.75rem 0.25rem; - border-radius: calc(infinity * 1px); - margin: 0; - border: 0; -} - -/* Tooltip Component Styles */ -media-tooltip { - color: oklab(1 0 0); - padding: 0.25rem 0.625rem; - border-radius: calc(infinity * 1px); - font-size: 0.75rem; -} - -.tooltip { - display: none; - white-space: nowrap; -} - -[data-paused] + media-tooltip .play-tooltip, -:not([data-paused]) + media-tooltip .pause-tooltip { - display: block; -} - -[data-fullscreen] + media-tooltip .fullscreen-exit-tooltip, -:not([data-fullscreen]) + media-tooltip .fullscreen-enter-tooltip { - display: block; -} diff --git a/packages/__tech-preview__/html/src/skins/minimal/index.ts b/packages/__tech-preview__/html/src/skins/minimal/index.ts deleted file mode 100644 index 9ec14ce1..00000000 --- a/packages/__tech-preview__/html/src/skins/minimal/index.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { MediaSkinElement } from '@/media/media-skin'; -import { defineCustomElement } from '@/utils/custom-element'; -import styles from './styles.css'; -// be sure to import video-provider first for proper context initialization -import '@/define/video-provider'; -import '@/define/media-container'; -import '@/define/media-play-button'; -import '@/define/media-mute-button'; -import '@/define/media-volume-slider'; -import '@/define/media-time-slider'; -import '@/define/media-fullscreen-button'; -import '@/define/media-duration-display'; -import '@/define/media-current-time-display'; -import '@/define/media-preview-time-display'; -import '@/define/media-popover'; - -import '@/define/media-tooltip'; -import '@/icons'; - -export function getTemplateHTML() { - return /* html */` - ${MediaSkinElement.getTemplateHTML()} - - - - - -
- -
- - - - - - - - Play - Pause - - -
- - - - - / - - -
- - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - Enter Fullscreen - Exit Fullscreen - -
-
-
- `; -} - -export class MediaSkinMinimalElement extends MediaSkinElement { - static getTemplateHTML: () => string = getTemplateHTML; -} - -defineCustomElement('media-skin-minimal', MediaSkinMinimalElement); diff --git a/packages/__tech-preview__/html/src/skins/minimal/styles.css b/packages/__tech-preview__/html/src/skins/minimal/styles.css deleted file mode 100644 index 94472e49..00000000 --- a/packages/__tech-preview__/html/src/skins/minimal/styles.css +++ /dev/null @@ -1,348 +0,0 @@ -media-container * { - box-sizing: border-box; -} - -media-container { - position: relative; - isolation: isolate; - container: root / inline-size; - overflow: clip; - border-radius: var(--minimal-border-radius, 0.75rem); - background: oklab(0 0 0); - font-family: ui-sans-serif, system-ui, sans-serif; - font-size: 0.8125rem; - line-height: 1.5; - -webkit-font-smoothing: auto; - -moz-osx-font-smoothing: auto; -} -media-container::after { - content: ''; - position: absolute; - pointer-events: none; - border-radius: inherit; - z-index: 10; - inset: 0; - box-shadow: inset 0 0 0 1px oklab(0 0 0 / 0.15); -} -@media (prefers-color-scheme: dark) { - media-container::after { - box-shadow: inset 0 0 0 1px oklab(1 0 0 / 0.15); - } -} - -/* Fullscreen */ -media-container:fullscreen { - border-radius: 0; -} - -media-container > ::slotted([slot='media']) { - display: block; - width: 100%; - height: 100%; -} - -/* Media Container UI Overlay Styling */ -media-container > .overlay { - position: absolute; - inset: 0; - display: flex; - flex-flow: column nowrap; - align-items: start; - pointer-events: none; - border-radius: inherit; - background-image: linear-gradient(to top, oklab(0 0 0 / 0.7), oklab(0 0 0 / 0.5) 7.5rem, rgba(0, 0, 0, 0)); - transition-property: opacity; - transition-duration: 75ms; - transition-timing-function: ease-out; - transition-delay: 0ms; -} -media-container[data-controls='hidden'] > .overlay { - opacity: 0; - transition-duration: 500ms; - transition-delay: 500ms; -} - -/* Media Control Bar UI/Styles */ -.control-bar { - position: absolute; - bottom: 0; - inset-inline: 0; - padding: 2rem 0.375rem 0.375rem 0.375rem; - display: flex; - align-items: center; - gap: 0.5rem; - will-change: transform, filter, opacity; - transition-property: transform, filter, opacity; - transition-delay: 0ms; - transition-duration: 75ms; - transition-timing-function: ease-out; - color: oklab(1 0 0); -} -media-container[data-controls='hidden'] > .control-bar { - opacity: 0; - transform: translateY(100%); - filter: blur(8px); - transition-delay: 500ms; - transition-duration: 500ms; - pointer-events: none; -} -@container root (width > 640px) { - .control-bar { - padding: 2.5rem 0.75rem 0.75rem 0.75rem; - gap: 0.875rem; - } -} - -/* Time Display Styling */ -.time-display-group { - display: flex; - align-items: center; - gap: 0.25rem; -} -.duration-display { - display: contents; - color: oklab(1 0 0 / 0.5); -} -media-current-time-display, -media-duration-display { - text-shadow: 0 1px 0 oklab(0 0 0 / 0.2); - font-variant-numeric: tabular-nums; -} - -.button-group { - display: flex; - align-items: center; - gap: 0.375rem; -} - -/* Generic Media Button Styling */ -.button { - display: grid; - flex-shrink: 0; - padding: 0.625rem; - cursor: pointer; - background: transparent; - border: none; - border-radius: 0.375rem; - color: oklab(1 0 0); - user-select: none; - outline: 2px solid transparent; - outline-offset: -2px; - transition-property: background-color, color, outline-offset; - transition-duration: 150ms; - transition-timing-function: ease-out; -} -.button:hover, -.button:focus-visible, -.button[aria-expanded='true'] { - color: oklab(1 0 0 / 0.8); - text-decoration: none; -} -.button:focus-visible { - outline-color: oklab(1 0 0); - outline-offset: 2px; -} -.button[disabled] { - cursor: not-allowed; - opacity: 0.5; - filter: grayscale(1); -} - -.button .icon { - grid-area: 1 / 1; - width: 18px; - height: 18px; - filter: drop-shadow(0 1px 0 oklab(0 0 0 / 0.4)); -} - -/* Media Play Button UI/Styles */ -media-play-button .icon { - opacity: 0; - transition: opacity 150ms linear; -} - -media-play-button:not([data-paused]) .pause-icon, -media-play-button[data-paused] .play-icon { - opacity: 1; -} - -/* Media Fullscreen Button UI/Styles */ -media-fullscreen-button .icon { - display: none; -} -media-fullscreen-button:not([data-fullscreen]) .fullscreen-enter-icon, -media-fullscreen-button[data-fullscreen] .fullscreen-exit-icon { - display: inline; -} - -/* One way to define the "default visible" icon (CJP) */ -media-mute-button .icon { - display: none; -} -media-mute-button:not([data-volume-level]) .volume-low-icon, -media-mute-button[data-volume-level='high'] .volume-high-icon, -media-mute-button[data-volume-level='low'] .volume-low-icon, -media-mute-button[data-volume-level='medium'] .volume-low-icon, -media-mute-button[data-volume-level='off'] .volume-off-icon { - display: inline; -} - -/* TimeSlider Component Styles */ -.slider { - flex: 1; - display: flex; - align-items: center; - justify-content: center; - position: relative; - border-radius: calc(infinity * 1px); - outline: none; -} - -/* Horizontal orientation styles */ -.slider[data-orientation='horizontal'] { - min-width: 5rem; - width: 100%; - height: 1.25rem; -} - -/* Vertical orientation styles */ -.slider[data-orientation='vertical'] { - height: 4.5rem; - width: 1.25rem; -} - -.slider-track { - position: relative; - isolation: isolate; - background-color: oklab(1 0 0 / 0.2); - border-radius: inherit; - overflow: hidden; - user-select: none; - outline: 2px solid transparent; - outline-offset: -2px; - transition: outline-offset 150ms ease-out; - box-shadow: 0 0 0 1px oklab(0 0 0 / 0.05); -} - -/* Horizontal track styles */ -.slider-track[data-orientation='horizontal'] { - width: 100%; - height: 0.1875rem; -} - -/* Vertical track styles */ -.slider-track[data-orientation='vertical'] { - width: 0.1875rem; - height: 100%; -} - -.slider:focus-visible .slider-track { - outline-color: oklab(1 0 0); - outline-offset: 6px; -} - -.slider-thumb { - width: 0.75rem; - height: 0.75rem; - background-color: oklab(1 0 0); - border-radius: calc(infinity * 1px); - user-select: none; - z-index: 10; - box-shadow: - 0 0 0 1px oklab(0 0 0 / 0.1), - 0 1px 3px 0 oklab(0 0 0 / 0.15), - 0 1px 2px -1px oklab(0 0 0 / 0.15); - opacity: 0; - scale: 0.7; - transform-origin: center; - transition-property: opacity, scale; - transition-duration: 150ms; - transition-timing-function: ease-out; -} -.slider:hover .slider-thumb, -.slider:focus-within .slider-thumb { - opacity: 1; - scale: 1; -} -.slider-track[data-orientation='horizontal'] .slider-thumb { - cursor: ew-resize; -} -.slider-track[data-orientation='vertical'] .slider-thumb { - cursor: ns-resize; -} - -.slider-pointer { - display: none; -} - -.slider-progress { - background-color: oklab(1 0 0); - border-radius: inherit; -} - -media-preview-time-display { - font-variant-numeric: tabular-nums; -} - -.popup-animation { - transition-property: transform, scale, opacity, filter; - transition-duration: 200ms; - transform: scale(1); - transform-origin: bottom; - opacity: 1; - filter: blur(0px); -} -.popup-animation[data-starting-style], -.popup-animation[data-ending-style] { - transform: scale(0); - opacity: 0; - filter: blur(8px); -} -.popup-animation[data-instant] { - transition-duration: 0ms; -} - -media-popover { - padding: 0.5rem; - margin: 0; - border: 0; - background: transparent; -} - -/* Tooltip Component Styles */ -media-tooltip { - color: oklab(1 0 0); - padding: 0.25rem 0.5rem; - border-radius: 0.25rem; - font-size: 0.75rem; - background-color: oklab(1 0 0 / 0.1); - backdrop-filter: blur(64px) brightness(0.9) saturate(1.5); - box-shadow: - 0 4px 6px -1px oklab(0 0 0 / 0.1), - 0 2px 4px -2px oklab(0 0 0 / 0.1); -} -@media (prefers-reduced-transparency: reduce) { - media-tooltip { - background-color: oklab(0 0 0 / 0.7); - } -} -@media (prefers-contrast: more) { - media-tooltip { - background-color: oklab(0 0 0 / 0.9); - } -} - -.tooltip { - display: none; - white-space: nowrap; -} - -[data-paused] + media-tooltip .play-tooltip, -:not([data-paused]) + media-tooltip .pause-tooltip { - display: block; -} - -[data-fullscreen] + media-tooltip .fullscreen-exit-tooltip, -:not([data-fullscreen]) + media-tooltip .fullscreen-enter-tooltip { - display: block; -} diff --git a/packages/__tech-preview__/html/src/store/index.ts b/packages/__tech-preview__/html/src/store/index.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/packages/__tech-preview__/html/src/types.d.ts b/packages/__tech-preview__/html/src/types.d.ts deleted file mode 100644 index b3340833..00000000 --- a/packages/__tech-preview__/html/src/types.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -declare module '*.css' { - const content: string; - export default content; -} - -export type Prettify = { - [K in keyof T]: T[K]; -} & {}; diff --git a/packages/__tech-preview__/html/src/utils/component-factory.ts b/packages/__tech-preview__/html/src/utils/component-factory.ts deleted file mode 100644 index 025b147e..00000000 --- a/packages/__tech-preview__/html/src/utils/component-factory.ts +++ /dev/null @@ -1,148 +0,0 @@ -import type { MediaStore } from '@videojs/core-preview/store'; -import { ConsumerMixin } from '@open-wc/context-protocol'; -import { shallowEqual, toCamelCase } from '@videojs/utils-preview'; -import { setAttributes } from '@videojs/utils-preview/dom'; - -/** - * Generic types for HTML component hooks pattern - * Mirrors the React hooks architecture for consistency - */ -export type StateHook = (element: E, mediaStore: MediaStore) => T; - -export type PropsHook = (element: E, state: T) => P; - -export interface ConnectedComponentConstructor { - new (state: State): E; -} - -let currentCoreInstances: any[] = []; -// There might be multiple getCoreState calls in a single state hook -// which should create a different core instance. -let currentCoreIndex: number = 0; - -/** - * Generic factory function to create connected HTML components using hooks pattern. - * Provides equivalent functionality to React's toConnectedComponent but for custom elements. - * - * @param BaseClass - Base custom element class to extend - * @param stateHook - Hook that defines state keys and transformation logic - * @param propsHook - Hook that handles element attributes and properties based on state - * @param displayName - Display name for debugging - * @returns Connected custom element class with media store integration - */ -export function toConnectedHTMLComponent( - BaseClass: CustomElementConstructor, - stateHook: StateHook | undefined, - propsHook: PropsHook, - displayName?: string, -): ConnectedComponentConstructor { - const ConnectedComponent = class extends ConsumerMixin(BaseClass) { - static get observedAttributes(): string[] { - return [ - // @ts-expect-error ts(2339) - ...(super.observedAttributes ?? []), - ]; - } - - _state: State | undefined; - #mediaStore: MediaStore | undefined; - #coreInstances: { core: any; listening: boolean }[] = []; - - contexts = { - mediaStore: (mediaStore: any) => { - this.#mediaStore = mediaStore; - - // Subscribe to media store state changes - mediaStore.subscribe(() => { - this.#render(); - - for (const instance of this.#coreInstances) { - if (!instance.listening) { - instance.listening = true; - instance.core.subscribe(this.#render); - } - } - }); - }, - }; - - #render = (): void => { - if (!this.#mediaStore) return; - - currentCoreIndex = 0; - currentCoreInstances = this.#coreInstances; - - // Split into two phases: state transformation, then props update - const state = stateHook?.(this as unknown as E, this.#mediaStore); - const props = propsHook(this as unknown as E, state ?? {} as State); - this._update(props, state, this.#mediaStore); - }; - - _update(props: any, state: State | undefined, _mediaStore: MediaStore): void { - this._state = state; - // @ts-expect-error any - super._update?.(props, state, _mediaStore); - setAttributes(this, props); - } - - attributeChangedCallback(name: string, oldValue: string, newValue: string): void { - super.attributeChangedCallback?.(name, oldValue, newValue); - this.#render(); - } - - connectedCallback(): void { - super.connectedCallback?.(); - } - - disconnectedCallback(): void { - super.disconnectedCallback?.(); - } - - handleEvent(event: CustomEvent): void { - // @ts-expect-error any - super.handleEvent?.(event); - } - }; - - // Set display name for debugging and dev tools - if (displayName) { - Object.defineProperty(ConnectedComponent, 'name', { value: displayName }); - } - - return ConnectedComponent as unknown as ConnectedComponentConstructor; -} - -export function getCoreState void) => () => void; - getState: () => any; - setState: (state: any) => void; -}>(CoreClass: new () => T, state: any): any { - let core = currentCoreInstances[currentCoreIndex]?.core as T; - if (!core) { - core = new CoreClass(); - currentCoreInstances[currentCoreIndex] = { core, listening: false }; - } - - currentCoreIndex++; - - const coreState = core.getState(); - const oldState: Record = {}; - for (const key in state) { - oldState[key] = coreState[key]; - } - // Only set the state if it has changed - if (!shallowEqual(oldState, state)) { - core.setState(state); - } - - return core.getState(); -} - -export function getPropsFromAttrs(element: HTMLElement): Record { - const props: Record = {}; - for (const attr of element.attributes) { - const camelName = toCamelCase(attr.name); - props[camelName] = element[camelName as keyof typeof element]; - } - return props; -} diff --git a/packages/__tech-preview__/html/src/utils/custom-element.ts b/packages/__tech-preview__/html/src/utils/custom-element.ts deleted file mode 100644 index 283ecb2a..00000000 --- a/packages/__tech-preview__/html/src/utils/custom-element.ts +++ /dev/null @@ -1,7 +0,0 @@ -export function defineCustomElement(tagName: string, ctor: CustomElementConstructor): void { - if (typeof window === 'undefined') return; - - if (window.customElements.get(tagName)) return; - - window.customElements.define(tagName, ctor); -} diff --git a/packages/__tech-preview__/html/tsconfig.build.json b/packages/__tech-preview__/html/tsconfig.build.json deleted file mode 100644 index b90fc83e..00000000 --- a/packages/__tech-preview__/html/tsconfig.build.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["src"] -} diff --git a/packages/__tech-preview__/html/tsconfig.json b/packages/__tech-preview__/html/tsconfig.json deleted file mode 100644 index 0c2d28cc..00000000 --- a/packages/__tech-preview__/html/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "baseUrl": ".", - "paths": { - "@/*": ["src/*"] - } - }, - "include": ["src"] -} diff --git a/packages/__tech-preview__/html/tsdown.config.ts b/packages/__tech-preview__/html/tsdown.config.ts deleted file mode 100644 index 6af0ef8a..00000000 --- a/packages/__tech-preview__/html/tsdown.config.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { readdirSync } from 'node:fs'; -import { fileURLToPath } from 'node:url'; -import { defineConfig } from 'tsdown'; - -const defineDir = fileURLToPath(new URL('./src/define', import.meta.url)); -const defineFiles = readdirSync(defineDir).filter(file => file.endsWith('.ts')); - -export default defineConfig({ - entry: { - index: 'src/index.ts', - store: 'src/store/index.ts', - icons: 'src/icons/index.ts', - 'skins/frosted': 'src/skins/frosted/index.ts', - 'skins/minimal': 'src/skins/minimal/index.ts', - ...defineFiles.reduce((entries, file) => { - const name = file.replace(/\.ts$/, ''); - entries[`define/${name}`] = `src/define/${file}`; - return entries; - }, {} as Record), - }, - platform: 'browser', - format: 'es', - sourcemap: true, - clean: true, - alias: { - '@': fileURLToPath(new URL('./src', import.meta.url)), - }, - dts: { - oxc: true, - }, - loader: { - '.svg': 'text', - '.css': 'text', - }, -}); diff --git a/packages/__tech-preview__/icons/CHANGELOG.md b/packages/__tech-preview__/icons/CHANGELOG.md deleted file mode 100644 index 81d5f550..00000000 --- a/packages/__tech-preview__/icons/CHANGELOG.md +++ /dev/null @@ -1,71 +0,0 @@ -# Changelog - -## [0.1.0-preview.10](https://github.com/videojs/v10/compare/@videojs/icons@0.1.0-preview.9...@videojs/icons@0.1.0-preview.10) (2025-12-06) - - -### Miscellaneous Chores - -* **@videojs/icons:** Synchronize videojs versions - -## [0.1.0-preview.9](https://github.com/videojs/v10/compare/@videojs/icons@0.1.0-preview.8...@videojs/icons@0.1.0-preview.9) (2025-11-18) - - -### Miscellaneous Chores - -* **@videojs/icons:** Synchronize videojs versions - -## [0.1.0-preview.8](https://github.com/videojs/v10/compare/@videojs/icons@0.1.0-preview.7...@videojs/icons@0.1.0-preview.8) (2025-11-12) - - -### Miscellaneous Chores - -* **@videojs/icons:** Synchronize videojs versions - -## [0.1.0-preview.7](https://github.com/videojs/v10/compare/@videojs/icons@0.1.0-preview.6...@videojs/icons@0.1.0-preview.7) (2025-11-11) - - -### Miscellaneous Chores - -* **@videojs/icons:** Synchronize videojs versions - -## [0.1.0-preview.6](https://github.com/videojs/v10/compare/@videojs/icons@0.1.0-preview.5...@videojs/icons@0.1.0-preview.6) (2025-11-06) - - -### Bug Fixes - -* **site:** update discord link ([#170](https://github.com/videojs/v10/issues/170)) ([bb10294](https://github.com/videojs/v10/commit/bb10294419439fb02df650f3e7f7c5496ecc3a73)) - -## [0.1.0-preview.5](https://github.com/videojs/v10/compare/@videojs/icons@0.1.0-preview.4...@videojs/icons@0.1.0-preview.5) (2025-11-03) - - -### Miscellaneous Chores - -* **@videojs/icons:** Synchronize videojs versions - -## [0.1.0-preview.4](https://github.com/videojs/v10/compare/@videojs/icons@0.1.0-preview.3...@videojs/icons@0.1.0-preview.4) (2025-10-30) - - -### Miscellaneous Chores - -* **@videojs/icons:** Synchronize videojs versions - -## [0.1.0-preview.3](https://github.com/videojs/v10/compare/@videojs/icons@0.1.0-preview.2...@videojs/icons@0.1.0-preview.3) (2025-10-29) - - -### Bug Fixes - -* **packages:** update version badges ([#138](https://github.com/videojs/v10/issues/138)) ([22d9cb6](https://github.com/videojs/v10/commit/22d9cb64f2e5b9601a2039bb166dbe3fee6a1b3e)) - -## [0.1.0-preview.2](https://github.com/videojs/v10/compare/@videojs/icons@0.1.0-preview.1...@videojs/icons@0.1.0-preview.2) (2025-10-25) - - -### Miscellaneous Chores - -* **@videojs/icons:** Synchronize videojs versions - -## [0.1.0-preview.1](https://github.com/videojs/v10/compare/@videojs/icons@0.1.0-preview.0...@videojs/icons@0.1.0-preview.1) (2025-10-25) - - -### Bug Fixes - -* **root:** add videojs keyword to package.json ([4a9f8b2](https://github.com/videojs/v10/commit/4a9f8b2ad6fb27b463dcfe8d1a5fd883c9fa21d1)) diff --git a/packages/__tech-preview__/icons/README.md b/packages/__tech-preview__/icons/README.md deleted file mode 100644 index 9c9dcf7e..00000000 --- a/packages/__tech-preview__/icons/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# @videojs/icons - -[![package-badge]][package] - -> **⚠️ Technical Preview - SUBJECT TO CHANGE** Not recommended for production use. - -## Overview - -`@videojs/icons` is a curated collection of SVG icons designed specifically for media player -interfaces. This package serves as the single source of truth for all Video.js icons, providing raw -SVG assets that platform-specific packages transform into their respective component formats. - -## Community - -If you need help with anything related to Video.js 10, or if you'd like to casually chat with other -members: - -- [Join Discord Server][discord] -- [See GitHub Discussions][gh-discussions] - -## License - -[Apache-2.0](./LICENSE) - -[package]: https://www.npmjs.com/package/@videojs/icons -[package-badge]: https://img.shields.io/npm/v/@videojs/icons/next?label=@videojs/icons@next -[discord]: https://discord.gg/JBqHh485uF -[gh-discussions]: https://github.com/videojs/v10/discussions diff --git a/packages/__tech-preview__/icons/assets/fullscreen-enter-alt.svg b/packages/__tech-preview__/icons/assets/fullscreen-enter-alt.svg deleted file mode 100644 index 63623fa5..00000000 --- a/packages/__tech-preview__/icons/assets/fullscreen-enter-alt.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - diff --git a/packages/__tech-preview__/icons/assets/fullscreen-enter.svg b/packages/__tech-preview__/icons/assets/fullscreen-enter.svg deleted file mode 100644 index fb646cd4..00000000 --- a/packages/__tech-preview__/icons/assets/fullscreen-enter.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - diff --git a/packages/__tech-preview__/icons/assets/fullscreen-exit-alt.svg b/packages/__tech-preview__/icons/assets/fullscreen-exit-alt.svg deleted file mode 100644 index 6c085818..00000000 --- a/packages/__tech-preview__/icons/assets/fullscreen-exit-alt.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - diff --git a/packages/__tech-preview__/icons/assets/fullscreen-exit.svg b/packages/__tech-preview__/icons/assets/fullscreen-exit.svg deleted file mode 100644 index 586fc235..00000000 --- a/packages/__tech-preview__/icons/assets/fullscreen-exit.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - diff --git a/packages/__tech-preview__/icons/assets/pause.svg b/packages/__tech-preview__/icons/assets/pause.svg deleted file mode 100644 index 94b7dc64..00000000 --- a/packages/__tech-preview__/icons/assets/pause.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/__tech-preview__/icons/assets/play.svg b/packages/__tech-preview__/icons/assets/play.svg deleted file mode 100644 index 9646cc51..00000000 --- a/packages/__tech-preview__/icons/assets/play.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/packages/__tech-preview__/icons/assets/spinner.svg b/packages/__tech-preview__/icons/assets/spinner.svg deleted file mode 100644 index 8ef97004..00000000 --- a/packages/__tech-preview__/icons/assets/spinner.svg +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/__tech-preview__/icons/assets/volume-high.svg b/packages/__tech-preview__/icons/assets/volume-high.svg deleted file mode 100755 index bae36d94..00000000 --- a/packages/__tech-preview__/icons/assets/volume-high.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - diff --git a/packages/__tech-preview__/icons/assets/volume-low.svg b/packages/__tech-preview__/icons/assets/volume-low.svg deleted file mode 100644 index 07fcbb44..00000000 --- a/packages/__tech-preview__/icons/assets/volume-low.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/packages/__tech-preview__/icons/assets/volume-off.svg b/packages/__tech-preview__/icons/assets/volume-off.svg deleted file mode 100644 index e776b6a9..00000000 --- a/packages/__tech-preview__/icons/assets/volume-off.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/packages/__tech-preview__/icons/package.json b/packages/__tech-preview__/icons/package.json deleted file mode 100644 index 7b0376e3..00000000 --- a/packages/__tech-preview__/icons/package.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "@videojs/icons-preview", - "type": "module", - "version": "0.1.0-preview.10", - "description": "SVG icon library for Video.js", - "license": "Apache-2.0", - "keywords": [ - "icons", - "svg", - "media", - "player", - "ui", - "videojs" - ], - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } - }, - "main": "dist/index.js", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist" - ], - "scripts": { - "build": "tsdown", - "build:watch": "tsdown --watch --silent", - "dev": "pnpm run build:watch", - "test": "echo \"No tests yet\"", - "clean": "rm -rf dist" - }, - "devDependencies": { - "tsdown": "^0.15.9", - "typescript": "^5.9.3" - }, - "publishConfig": { - "access": "public" - } -} diff --git a/packages/__tech-preview__/icons/src/index.ts b/packages/__tech-preview__/icons/src/index.ts deleted file mode 100644 index 27221111..00000000 --- a/packages/__tech-preview__/icons/src/index.ts +++ /dev/null @@ -1,93 +0,0 @@ -import fullscreenEnterAlt from '../assets/fullscreen-enter-alt.svg'; -// Import SVG files as strings -import fullscreenEnter from '../assets/fullscreen-enter.svg'; -import fullscreenExitAlt from '../assets/fullscreen-exit-alt.svg'; -import fullscreenExit from '../assets/fullscreen-exit.svg'; -import pause from '../assets/pause.svg'; -import play from '../assets/play.svg'; -import volumeHigh from '../assets/volume-high.svg'; -import volumeLow from '../assets/volume-low.svg'; -import volumeOff from '../assets/volume-off.svg'; - -export const SVG_ICONS: Record = { - play, - pause, - volumeHigh, - volumeLow, - volumeOff, - fullscreenEnter, - fullscreenExit, - fullscreenEnterAlt, - fullscreenExitAlt, -}; - -// TODO: Check if we need below? It seems to be unused now. - -// Legacy interface for backward compatibility -export interface IconDefinition { - name: string; - viewBox: string; - paths: string[]; -} - -export const ICON_DEFINITIONS: Record = { - play: { - name: 'play', - viewBox: '0 0 24 24', - paths: ['M8 5v14l11-7z'], - }, - pause: { - name: 'pause', - viewBox: '0 0 24 24', - paths: ['M6 19h4V5H6v14zm8-14v14h4V5h-4z'], - }, - stop: { - name: 'stop', - viewBox: '0 0 24 24', - paths: ['M6 6h12v12H6z'], - }, - volumeUp: { - name: 'volume-up', - viewBox: '0 0 24 24', - paths: [ - 'M3 9v6h4l5 5V4L7 9H3z', - 'M16.5 12c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z', - 'M14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z', - ], - }, - volumeOff: { - name: 'volume-off', - viewBox: '0 0 24 24', - paths: [ - 'M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63z', - 'M19 12c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71z', - 'M4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3z', - 'M12 4L9.91 6.09 12 8.18V4z', - ], - }, - fullscreenEnter: { - name: 'fullscreen-enter', - viewBox: '0 0 26 24', - paths: ['M16 3v2.5h3.5V9H22V3h-6ZM4 9h2.5V5.5H10V3H4v6Zm15.5 9.5H16V21h6v-6h-2.5v3.5ZM6.5 15H4v6h6v-2.5H6.5V15Z'], - }, - fullscreenExit: { - name: 'fullscreen-exit', - viewBox: '0 0 26 24', - paths: [ - 'M18.5 6.5V3H16v6h6V6.5h-3.5ZM16 21h2.5v-3.5H22V15h-6v6ZM4 17.5h3.5V21H10v-6H4v2.5Zm3.5-11H4V9h6V3H7.5v3.5Z', - ], - }, -}; - -export function getIcon(name: string): IconDefinition | undefined { - return ICON_DEFINITIONS[name]; -} - -export function getAllIcons(): IconDefinition[] { - return Object.values(ICON_DEFINITIONS); -} - -export function createSVGString(icon: IconDefinition): string { - const pathElements = icon.paths.map(path => ``).join(''); - return `${pathElements}`; -} diff --git a/packages/__tech-preview__/icons/src/svg.d.ts b/packages/__tech-preview__/icons/src/svg.d.ts deleted file mode 100644 index cdb2b1a9..00000000 --- a/packages/__tech-preview__/icons/src/svg.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module '*.svg' { - const content: string; - export default content; -} diff --git a/packages/__tech-preview__/icons/tsconfig.build.json b/packages/__tech-preview__/icons/tsconfig.build.json deleted file mode 100644 index b90fc83e..00000000 --- a/packages/__tech-preview__/icons/tsconfig.build.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["src"] -} diff --git a/packages/__tech-preview__/icons/tsconfig.json b/packages/__tech-preview__/icons/tsconfig.json deleted file mode 100644 index c4d7be0c..00000000 --- a/packages/__tech-preview__/icons/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "baseUrl": ".", - "paths": { - "@/*": ["src/*"] - } - }, - "include": ["src"] -} diff --git a/packages/__tech-preview__/icons/tsdown.config.ts b/packages/__tech-preview__/icons/tsdown.config.ts deleted file mode 100644 index e0277064..00000000 --- a/packages/__tech-preview__/icons/tsdown.config.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { defineConfig } from 'tsdown'; - -export default defineConfig({ - entry: 'src/index.ts', - platform: 'neutral', - format: 'es', - sourcemap: true, - clean: true, - alias: { - '@': new URL('./src', import.meta.url).pathname, - }, - dts: { - oxc: true, - }, - loader: { - '.svg': 'text', - }, -}); diff --git a/packages/__tech-preview__/react-native/.gitkeep b/packages/__tech-preview__/react-native/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/packages/__tech-preview__/react/CHANGELOG.md b/packages/__tech-preview__/react/CHANGELOG.md deleted file mode 100644 index ab243f15..00000000 --- a/packages/__tech-preview__/react/CHANGELOG.md +++ /dev/null @@ -1,253 +0,0 @@ -# Changelog - -## [0.1.0-preview.10](https://github.com/videojs/v10/compare/@videojs/react@0.1.0-preview.9...@videojs/react@0.1.0-preview.10) (2025-12-06) - - -### Features - -* add console banner ([#186](https://github.com/videojs/v10/issues/186)) ([072b0de](https://github.com/videojs/v10/commit/072b0dece76289d7d4215907ce70b36debbb78bc)) -* add tooltip core ([#212](https://github.com/videojs/v10/issues/212)) ([cbf41ce](https://github.com/videojs/v10/commit/cbf41ce4c750cd5c3bb6cbf247bad91ccd578cd0)) - - -### Bug Fixes - -* use popover core in react popover ([#208](https://github.com/videojs/v10/issues/208)) ([99fef78](https://github.com/videojs/v10/commit/99fef78f63e8dd121513b9cd20696a7d35603837)) - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/core bumped to 0.1.0-preview.10 - * @videojs/icons bumped to 0.1.0-preview.10 - * @videojs/utils bumped to 0.1.0-preview.10 - -## [0.1.0-preview.9](https://github.com/videojs/v10/compare/@videojs/react@0.1.0-preview.8...@videojs/react@0.1.0-preview.9) (2025-11-18) - - -### Bug Fixes - -* anchor name in popover and tooltip ([#194](https://github.com/videojs/v10/issues/194)) ([d965858](https://github.com/videojs/v10/commit/d9658582f12f9745548bcb9461a3e365adc16339)) -* clean up core, less seams in wrappers ([#197](https://github.com/videojs/v10/issues/197)) ([601a5a3](https://github.com/videojs/v10/commit/601a5a37939605ca38f81a825fd02934e7928c39)) -* fix CLS due to popover attribute not SSR ([#202](https://github.com/videojs/v10/issues/202)) ([0952673](https://github.com/videojs/v10/commit/09526731f7e4ee3bd58c45179d37f90dfc96d7a8)) - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/core bumped to 0.1.0-preview.9 - * @videojs/icons bumped to 0.1.0-preview.9 - * @videojs/utils bumped to 0.1.0-preview.9 - -## [0.1.0-preview.8](https://github.com/videojs/v10/compare/@videojs/react@0.1.0-preview.7...@videojs/react@0.1.0-preview.8) (2025-11-12) - - -### Bug Fixes - -* hydration mismatch in Tooltip and Popover ([#190](https://github.com/videojs/v10/issues/190)) ([8b868e9](https://github.com/videojs/v10/commit/8b868e95a3ffd92421ddb2846708d3a2ca8125e3)) - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/core bumped to 0.1.0-preview.8 - * @videojs/icons bumped to 0.1.0-preview.8 - * @videojs/utils bumped to 0.1.0-preview.8 - -## [0.1.0-preview.7](https://github.com/videojs/v10/compare/@videojs/react@0.1.0-preview.6...@videojs/react@0.1.0-preview.7) (2025-11-11) - - -### Features - -* **react:** use popover and anchor position API ([#178](https://github.com/videojs/v10/issues/178)) ([f513c74](https://github.com/videojs/v10/commit/f513c74a6cebc09ac76512bb86af10a33350f900)) - - -### Bug Fixes - -* **skins:** remove vjs- prefixed CSS custom properties ([#179](https://github.com/videojs/v10/issues/179)) ([a62623b](https://github.com/videojs/v10/commit/a62623ba41d294edead981a1b468f7f486c91625)) -* **skins:** slightly more idiomatic Tailwind, added custom properties ([#175](https://github.com/videojs/v10/issues/175)) ([694afb7](https://github.com/videojs/v10/commit/694afb7de992edbd667bc3d895df8bf198ccabb3)) - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/core bumped to 0.1.0-preview.7 - * @videojs/icons bumped to 0.1.0-preview.7 - * @videojs/utils bumped to 0.1.0-preview.7 - -## [0.1.0-preview.6](https://github.com/videojs/v10/compare/@videojs/react@0.1.0-preview.5...@videojs/react@0.1.0-preview.6) (2025-11-06) - - -### Features - -* **react:** use SimpleVideo as default Video and rename HLS version to HlsVideo ([#171](https://github.com/videojs/v10/issues/171)) ([1878a27](https://github.com/videojs/v10/commit/1878a271ba300919d1eff19dd2db79340e8714fa)) - - -### Bug Fixes - -* **react, html:** rename MediaProvider (and related) to VideoProvider ([#159](https://github.com/videojs/v10/issues/159)) ([f285573](https://github.com/videojs/v10/commit/f28557359c7dda0282f9d532fa4b2fce19766e40)) -* **react:** update use client usage in react package modules for next.js use ([#157](https://github.com/videojs/v10/issues/157)) ([16cfde1](https://github.com/videojs/v10/commit/16cfde138d5a8c9fb919c49f49240ebacb1d0639)) -* **site:** update discord link ([#170](https://github.com/videojs/v10/issues/170)) ([bb10294](https://github.com/videojs/v10/commit/bb10294419439fb02df650f3e7f7c5496ecc3a73)) - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/core bumped to 0.1.0-preview.6 - * @videojs/icons bumped to 0.1.0-preview.6 - * @videojs/utils bumped to 0.1.0-preview.6 - -## [0.1.0-preview.5](https://github.com/videojs/v10/compare/@videojs/react@0.1.0-preview.4...@videojs/react@0.1.0-preview.5) (2025-11-03) - - -### Miscellaneous Chores - -* **@videojs/react:** Synchronize videojs versions - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/core bumped to 0.1.0-preview.5 - * @videojs/icons bumped to 0.1.0-preview.5 - * @videojs/utils bumped to 0.1.0-preview.5 - -## [0.1.0-preview.4](https://github.com/videojs/v10/compare/@videojs/react@0.1.0-preview.3...@videojs/react@0.1.0-preview.4) (2025-10-30) - - -### Features - -* **html:** add element registrations ([eaaa03c](https://github.com/videojs/v10/commit/eaaa03c284f9fc3c9069bedb74d95c8a951c1b56)) - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/core bumped to 0.1.0-preview.4 - * @videojs/icons bumped to 0.1.0-preview.4 - * @videojs/utils bumped to 0.1.0-preview.4 - -## [0.1.0-preview.3](https://github.com/videojs/v10/compare/@videojs/react@0.1.0-preview.2...@videojs/react@0.1.0-preview.3) (2025-10-29) - - -### Features - -* **skins:** add html port of minimal skin ([#140](https://github.com/videojs/v10/issues/140)) ([47ac22e](https://github.com/videojs/v10/commit/47ac22e9b8a513c9640ed1eff20838d8cdb749ce)) -* **ui:** skin design improvements, add html frosted skin (WIP) ([#133](https://github.com/videojs/v10/issues/133)) ([75a90e7](https://github.com/videojs/v10/commit/75a90e71041c2d0bf434823e59dd04f86f89b0bb)) - - -### Bug Fixes - -* add aspect-ratio to demos ([#136](https://github.com/videojs/v10/issues/136)) ([e0a46de](https://github.com/videojs/v10/commit/e0a46de0ab56199afd37b24ebfce93ee85e2b699)) -* **packages:** update version badges ([#138](https://github.com/videojs/v10/issues/138)) ([22d9cb6](https://github.com/videojs/v10/commit/22d9cb64f2e5b9601a2039bb166dbe3fee6a1b3e)) -* **react:** prevent dev build race condition ([#139](https://github.com/videojs/v10/issues/139)) ([67d1007](https://github.com/videojs/v10/commit/67d1007d0c78564537f38f1683a45af320869465)) - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/core bumped to 0.1.0-preview.3 - * @videojs/icons bumped to 0.1.0-preview.3 - * @videojs/utils bumped to 0.1.0-preview.3 - -## [0.1.0-preview.2](https://github.com/videojs/v10/compare/@videojs/react@0.1.0-preview.1...@videojs/react@0.1.0-preview.2) (2025-10-25) - - -### Miscellaneous Chores - -* **@videojs/react:** Synchronize videojs versions - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/core bumped to 0.1.0-preview.2 - * @videojs/icons bumped to 0.1.0-preview.2 - * @videojs/utils bumped to 0.1.0-preview.2 - -## [0.1.0-preview.1](https://github.com/videojs/v10/compare/@videojs/react@0.1.0-preview.0...@videojs/react@0.1.0-preview.1) (2025-10-25) - - -### Features - -* add a solution for React preview time display ([#50](https://github.com/videojs/v10/issues/50)) ([f78b09f](https://github.com/videojs/v10/commit/f78b09fd16b7a9ee5a404c9260e3e764fb77ddde)) -* add compound html timerange component ([#14](https://github.com/videojs/v10/issues/14)) ([017ecdb](https://github.com/videojs/v10/commit/017ecdbff991d140ea42e4a855269a54e0a19adc)) -* add core range, time and volume range ([#23](https://github.com/videojs/v10/issues/23)) ([687b765](https://github.com/videojs/v10/commit/687b7655b0b6356c28663ca85c8f6d25a1023c18)) -* add data style attributes to popover ([#62](https://github.com/videojs/v10/issues/62)) ([6d7325c](https://github.com/videojs/v10/commit/6d7325cedb182c37b955e729d32204e4afbba948)) -* add display click to play / pause ([#117](https://github.com/videojs/v10/issues/117)) ([4f06ef6](https://github.com/videojs/v10/commit/4f06ef6c7684fd7064ca76685003a1c38ebd09cd)) -* add focus state to sliders and volume slider ([#60](https://github.com/videojs/v10/issues/60)) ([f514051](https://github.com/videojs/v10/commit/f514051263f95d892315eed9d44b3d83829e5d4b)) -* add HTML tooltip component ([#40](https://github.com/videojs/v10/issues/40)) ([d2a0b27](https://github.com/videojs/v10/commit/d2a0b27272f1967e5f7cde2df12920af92e3c300)) -* add keyboard control to sliders ([#115](https://github.com/videojs/v10/issues/115)) ([0a49026](https://github.com/videojs/v10/commit/0a4902623d58f51055b1cc65498a0e716533ec29)) -* add popover React component ([#33](https://github.com/videojs/v10/issues/33)) ([f775a3d](https://github.com/videojs/v10/commit/f775a3daf7573efb7fdbb15e96cf100daba4a487)) -* add range orientation to react components ([#30](https://github.com/videojs/v10/issues/30)) ([5e3cb8a](https://github.com/videojs/v10/commit/5e3cb8ad8134ecaf20b6e021e301a38b8ed06de2)) -* add React tooltip component ([#35](https://github.com/videojs/v10/issues/35)) ([c4a3f1b](https://github.com/videojs/v10/commit/c4a3f1b2f61d67f3346acd4aa2bf13630a6283eb)) -* add showRemaining functionality to current time display ([ab07a8a](https://github.com/videojs/v10/commit/ab07a8a9a26f68066e292718f3cd17a2640c5477)) -* add transition status to React tooltip ([#42](https://github.com/videojs/v10/issues/42)) ([39e862a](https://github.com/videojs/v10/commit/39e862a48704d31c9c1ca932a789438e3c54ec31)) -* add volume range compound component ([#19](https://github.com/videojs/v10/issues/19)) ([aefe890](https://github.com/videojs/v10/commit/aefe890fee93981542282087b2f2c7474f1b47e6)) -* **core,html,react:** implement VolumeRange component with integrated state management ([2282f47](https://github.com/videojs/v10/commit/2282f4799b1c3fc3c55473bdfc2def86384d5d19)) -* implement current time display components ([5bd0a15](https://github.com/videojs/v10/commit/5bd0a154dbba01d2a5d11eb1f548fe4baa581675)) -* initialize Video.js 10 monorepo with core architecture ([4b0d84e](https://github.com/videojs/v10/commit/4b0d84e9c8adfa7401084389da5deb751420b629)) -* make time range compound component ([#10](https://github.com/videojs/v10/issues/10)) ([71a8daf](https://github.com/videojs/v10/commit/71a8dafd741c50fb489707678e23807fabe4cb4e)) -* **media-store,html,react:** implement TimeRange component with hook-style architecture ([c29fd2c](https://github.com/videojs/v10/commit/c29fd2c2c1edd61c09a6683041c709a990d8a6f0)) -* migrate entire monorepo from tsc to tsup for production builds ([7403cc7](https://github.com/videojs/v10/commit/7403cc728119322888e527468a07a7634f43b32a)) -* migrate examples from prototype and add CSS modules support ([19c85fc](https://github.com/videojs/v10/commit/19c85fc2f8eb50c04dc6fa40322a2bd03358ad2e)) -* **monorepo:** migrate prototype code to organized package structure ([4b472ec](https://github.com/videojs/v10/commit/4b472ec49cd91f0af61cb5aaa039d428982d3b91)) -* **react-icons:** implement SVGR-powered auto-generation with full styling support ([6fcb18f](https://github.com/videojs/v10/commit/6fcb18f1d2b108990025f8ea67b3a31e17879d49)) -* **react-media-store:** add shallowEqual utility for optimized state comparisons ([8e75d62](https://github.com/videojs/v10/commit/8e75d62f629b9320aeef48801856e1afb6c341ef)) -* **react:** add fullscreen button component ([7d819df](https://github.com/videojs/v10/commit/7d819dfcd97a545ee46e1a62a20e553896b6e92e)) -* **react:** add MediaContainer component for fullscreen functionality ([23e3876](https://github.com/videojs/v10/commit/23e38766308a21a7e5b17196344159f06581813c)) -* **react:** add Tailwind v4 compiled CSS for skins with vjs prefix ([#114](https://github.com/videojs/v10/issues/114)) ([92b82e9](https://github.com/videojs/v10/commit/92b82e94f9cc856164f3b1c1a4e356fb2b07a73f)) -* **react:** adding simple video ([#125](https://github.com/videojs/v10/issues/125)) ([f6b5f80](https://github.com/videojs/v10/commit/f6b5f804bf92e3e4573a8e93f7051636dcc36143)) -* **react:** enable automatic CSS injection for MediaSkinDefault component ([bc8adaa](https://github.com/videojs/v10/commit/bc8adaa508802e9db95953e32963fe0e88107acf)) -* **react:** implement duration display component ([c5b659f](https://github.com/videojs/v10/commit/c5b659f4493787cadead2196c41e7f697c9737c7)) -* rename range to slider ([#46](https://github.com/videojs/v10/issues/46)) ([9c6eaef](https://github.com/videojs/v10/commit/9c6eaef2aa61771ae1407d0a594b3f790e0ff665)) -* **skins:** integrate duration display into default skins ([2ed2191](https://github.com/videojs/v10/commit/2ed219158ddf6f720ce1be8e328bec4603a7e847)) -* **ui:** add toasted skin ([bdd2f36](https://github.com/videojs/v10/commit/bdd2f36e13cf53fb2cec61730dece42a1ce8ce84)) -* **ui:** micro icons, toasted design tweaks ([#52](https://github.com/videojs/v10/issues/52)) ([bd3f0f7](https://github.com/videojs/v10/commit/bd3f0f7510480125653506d8e2e560234f6c06f2)) -* **ui:** minor style tweaks ([d3221ec](https://github.com/videojs/v10/commit/d3221ec21181090f7c5694f1951636c13b4c330b)) -* **ui:** more skin style tweaks ([#53](https://github.com/videojs/v10/issues/53)) ([7c9a0a5](https://github.com/videojs/v10/commit/7c9a0a5e3e838c430c073b0b36dff3a81ad9aa46)) -* **ui:** port over default skin ([ff4ea36](https://github.com/videojs/v10/commit/ff4ea3693e63ab3b5a728988ca44f3bab669e8ff)) -* **ui:** port over default skin ([9950945](https://github.com/videojs/v10/commit/995094500823e1063e7ae291c9a2ea9a4aa74847)) -* **ui:** skin and icon tweaks ([#59](https://github.com/videojs/v10/issues/59)) ([cdebece](https://github.com/videojs/v10/commit/cdebece1ebe8e5160b90e49ac2cbf05a7e0b6dca)) -* **ui:** skin design tweaks ([#126](https://github.com/videojs/v10/issues/126)) ([a93484e](https://github.com/videojs/v10/commit/a93484e86b1d9209e3c49bfc6b2f5b11db55d7af)) -* **ui:** styling fixes for toasted skin ([#38](https://github.com/videojs/v10/issues/38)) ([4ffaa58](https://github.com/videojs/v10/commit/4ffaa586340688ac9f5fb6d2ae3c02a62e9c4ab7)) - - -### Bug Fixes - -* design tweaks to toasted skin, lint rule tweaks ([#44](https://github.com/videojs/v10/issues/44)) ([3a0767c](https://github.com/videojs/v10/commit/3a0767c3407b2d6d8af3d3a8afd57b1e76efda85)) -* enable eslint & run eslint:fix ([#43](https://github.com/videojs/v10/issues/43)) ([5cb93a1](https://github.com/videojs/v10/commit/5cb93a14a7f47d66d5c71f9b82867621beda236c)) -* **icons:** add currentColor fill to fullscreen icons for proper theming ([d0d4876](https://github.com/videojs/v10/commit/d0d487601eb2da669be9a83cc8201d94998ec334)) -* minimal volume slider bug & fix dev infinite bug ([#73](https://github.com/videojs/v10/issues/73)) ([591dab6](https://github.com/videojs/v10/commit/591dab66caf8829017688007320f92b7445c4baa)) -* React version mismatch, add forward refs ([0a42bfe](https://github.com/videojs/v10/commit/0a42bfe86d1432c30a9f632a4235e4317c5b87e9)) -* **react:** implement proper HTML boolean data attributes for components ([26998a1](https://github.com/videojs/v10/commit/26998a121710d57c56eaf8dfe9f423a4fe91a401)) -* resolve package dependency and TypeScript export issues ([7154b1e](https://github.com/videojs/v10/commit/7154b1e44674a61735ab0f393a8bed3fcc689f8d)) -* resolve TypeScript build errors across packages ([374db7a](https://github.com/videojs/v10/commit/374db7afc07d6211bfd3f8079bbcd9613f3b69f3)) -* **root:** add videojs keyword to package.json ([4a9f8b2](https://github.com/videojs/v10/commit/4a9f8b2ad6fb27b463dcfe8d1a5fd883c9fa21d1)) -* seek jump back to current time ([#22](https://github.com/videojs/v10/issues/22)) ([a3f9630](https://github.com/videojs/v10/commit/a3f9630bd1eb34a16f339ffd30071b8adc864ca0)) -* skin exports/imports ([0afa9b7](https://github.com/videojs/v10/commit/0afa9b7187088b42408e6c64eff9026484473183)) -* skin syntax usage cleanup ([#48](https://github.com/videojs/v10/issues/48)) ([d4f3f4b](https://github.com/videojs/v10/commit/d4f3f4b75b2c94c47b18242f131ac7050eb54cfc)) -* **time-display:** clean up time utilities and simplify components ([597e79d](https://github.com/videojs/v10/commit/597e79d7fc12737353c8c9eb3f6e77ef0a04e9ed)) -* tooltip and popover portal ([49d0b03](https://github.com/videojs/v10/commit/49d0b03d899c61b6c980a198ed531baeafe4dde7)) -* tooltip syntax error & remove restMs ([fee1670](https://github.com/videojs/v10/commit/fee16708cb13670e30c8628425d621e2cfe0c7c6)) -* **typescript:** resolve declaration file generation for rollup packages ([#1](https://github.com/videojs/v10/issues/1)) ([69670e8](https://github.com/videojs/v10/commit/69670e8d7134db34aee665d8871cd17901625915)) -* **ui:** revert style testing change ([1ef66c9](https://github.com/videojs/v10/commit/1ef66c94611ad46f0f0b2f2fea0e0f0aa3e268f0)) -* **ui:** tone down text shadow on toasted skin ([#54](https://github.com/videojs/v10/issues/54)) ([924b257](https://github.com/videojs/v10/commit/924b2578a41ad90df2c674e48016ff0aaa58287e)) -* **workspace:** convert pnpm workspace protocol to npm workspace syntax ([fc5a0e4](https://github.com/videojs/v10/commit/fc5a0e46fd15f30245cb743a8006fc097c5b890e)) - - -### Dependencies - -* The following workspace dependencies were updated - * dependencies - * @videojs/core bumped to 0.1.0-preview.1 - * @videojs/icons bumped to 0.1.0-preview.1 - * @videojs/utils bumped to 0.1.0-preview.1 diff --git a/packages/__tech-preview__/react/README.md b/packages/__tech-preview__/react/README.md deleted file mode 100644 index 297c45f1..00000000 --- a/packages/__tech-preview__/react/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# @videojs/react - -[![package-badge]][package] - -> **⚠️ Technical Preview - SUBJECT TO CHANGE** Not recommended for production use. - -## Overview - -`@videojs/react` is a comprehensive library for building media players in React applications. It -provides a complete set of components, hooks, and utilities for creating feature-rich, accessible -video and audio players with React. - -## Community - -If you need help with anything related to Video.js 10, or if you'd like to casually chat with other -members: - -- [Join Discord Server][discord] -- [See GitHub Discussions][gh-discussions] - -## License - -[Apache-2.0](./LICENSE) - -[package]: https://www.npmjs.com/package/@videojs/react -[package-badge]: https://img.shields.io/npm/v/@videojs/react/next?label=@videojs/react@next -[discord]: https://discord.gg/JBqHh485uF -[gh-discussions]: https://github.com/videojs/v10/discussions diff --git a/packages/__tech-preview__/react/build/build-styles.ts b/packages/__tech-preview__/react/build/build-styles.ts deleted file mode 100644 index 4da8a521..00000000 --- a/packages/__tech-preview__/react/build/build-styles.ts +++ /dev/null @@ -1,151 +0,0 @@ -#!/usr/bin/env node - -/** - * Build Styles Script - * - * This unified script handles the complete CSS build process for skins: - * - * 1. COMPILATION: Runs Tailwind CLI to compile CSS files - * - preflight.css (browser resets) - * - frosted.css (frosted skin) - * - minimal.css (minimal skin) - * - * 2. SCOPING: Scopes preflight selectors to `.vjs` class to prevent global resets - * - Transforms `* { margin: 0 }` → `.vjs * { margin: 0 }` - * - Special cases like `html`, `body`, `:root` → `.vjs` - * - * 3. CONCATENATION: Prepends scoped preflight to each skin CSS file - * - frosted.css = scoped-preflight + frosted styles - * - minimal.css = scoped-preflight + minimal styles - * - Deletes standalone preflight.css (no longer needed) - * - * Why scope preflight? - * - Tailwind's preflight resets affect ALL elements globally - * - Scoping to `.vjs` ensures resets only apply inside video player containers - * - * Why concatenate? - * - Users only need to import one CSS file per skin - * - Simpler API: import '@videojs/react-preview/skins/frosted.css' - */ - -import { execSync } from 'node:child_process'; -import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs'; -import { dirname, join } from 'node:path'; -import process from 'node:process'; -import { fileURLToPath } from 'node:url'; -import postcss from 'postcss'; -import prefixSelector from 'postcss-prefix-selector'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); -const rootDir = join(__dirname, '..'); - -const preflightFile = 'dist/skins/preflight.css'; -const frostedFile = 'dist/skins/frosted.css'; -const minimalFile = 'dist/skins/minimal.css'; - -export default async function buildStyles() { - console.log('🎨 Building skin styles...'); - - try { - // ============================================================================ - // STEP 1: Compile CSS files with Tailwind CLI - // ============================================================================ - console.log(' → Compiling CSS with Tailwind...'); - - // Create output directory - const execOpts = { cwd: rootDir, stdio: 'inherit', encoding: 'utf8' }; - const skinsDir = join(rootDir, 'dist', 'skins'); - - mkdirSync(skinsDir, { recursive: true }); - - // Compile preflight (browser resets) - execSync( - 'pnpm exec tailwindcss -i src/skins/preflight.css -o dist/skins/preflight.css --minify', - execOpts, - ); - - // Compile frosted skin - execSync( - 'pnpm exec tailwindcss -i src/skins/frosted/frosted-skin.css -o dist/skins/frosted.css --minify', - execOpts, - ); - - // Compile minimal skin - execSync( - 'pnpm exec tailwindcss -i src/skins/minimal/minimal-skin.css -o dist/skins/minimal.css --minify', - execOpts, - ); - - // Verify all CSS files were created - const preflightPath = join(rootDir, preflightFile); - const frostedPath = join(rootDir, frostedFile); - const minimalPath = join(rootDir, minimalFile); - - if (!existsSync(preflightPath)) { - throw new Error(`Tailwind failed to create ${preflightFile}`); - } - if (!existsSync(frostedPath)) { - throw new Error(`Tailwind failed to create ${frostedFile}`); - } - if (!existsSync(minimalPath)) { - throw new Error(`Tailwind failed to create ${minimalFile}`); - } - - // ============================================================================ - // STEP 2: Scope preflight CSS to .vjs - // ============================================================================ - console.log(' → Scoping preflight selectors to .vjs...'); - - const preflightCss = readFileSync(preflightPath, 'utf-8'); - - // Use PostCSS with postcss-prefix-selector plugin to transform all selectors - const result = await postcss([ - prefixSelector({ - prefix: '.vjs', - transform(prefix, selector, prefixedSelector) { - // Special case transformations: - // - `html`, `body`, `:root` should become just `.vjs` (not `.vjs html`) - if (selector === 'body' || selector === 'html' || selector === ':root') { - return prefix; - } else if (selector === ':host') { - // Keep :host as-is (for web components) - return selector; - } else { - // All other selectors: prepend .vjs - return prefixedSelector; - } - }, - }), - ]).process(preflightCss, { from: preflightFile, to: preflightFile }); - - // Clean up any duplicate .vjs classes that may have been generated - const scopedPreflight = result.css.replace(/(\.vjs\s+)+/g, '.vjs '); - - // ============================================================================ - // STEP 3: Concatenate scoped preflight into each skin - // ============================================================================ - console.log(' → Concatenating preflight into frosted.css...'); - - const frostedCss = readFileSync(frostedPath, 'utf-8'); - const frostedWithPreflight = `${scopedPreflight}\n${frostedCss}`; - writeFileSync(frostedPath, frostedWithPreflight, 'utf-8'); - - console.log(' → Concatenating preflight into minimal.css...'); - - const minimalCss = readFileSync(minimalPath, 'utf-8'); - const minimalWithPreflight = `${scopedPreflight}\n${minimalCss}`; - writeFileSync(minimalPath, minimalWithPreflight, 'utf-8'); - - // ============================================================================ - // STEP 4: Clean up - delete standalone preflight.css - // ============================================================================ - console.log(' → Removing standalone preflight.css...'); - unlinkSync(preflightPath); - - console.log('✅ Skin styles built successfully'); - } catch (error) { - console.error('❌ Failed to build styles:', error.message); - process.exit(1); - } -} diff --git a/packages/__tech-preview__/react/package.json b/packages/__tech-preview__/react/package.json deleted file mode 100644 index 7d808d2d..00000000 --- a/packages/__tech-preview__/react/package.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "name": "@videojs/react-preview", - "type": "module", - "version": "0.1.0-preview.10", - "description": "React library for building media players", - "license": "Apache-2.0", - "keywords": [ - "media", - "player", - "react", - "components", - "hooks", - "videojs" - ], - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "./store": { - "types": "./dist/store.d.ts", - "default": "./dist/store.js" - }, - "./icons": { - "types": "./dist/icons.d.ts", - "default": "./dist/icons.js" - }, - "./skins/frosted": { - "types": "./dist/skins/frosted.d.ts", - "default": "./dist/skins/frosted.js" - }, - "./skins/minimal": { - "types": "./dist/skins/minimal.d.ts", - "default": "./dist/skins/minimal.js" - }, - "./skins/frosted.css": "./dist/skins/frosted.css", - "./skins/minimal.css": "./dist/skins/minimal.css" - }, - "main": "dist/index.js", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist" - ], - "scripts": { - "build": "tsdown", - "build:watch": "tsdown --watch ./src --ignore-watch ./src/icons/generated-icons --silent", - "dev": "pnpm run build:watch", - "format:icons": "echo noop. Need to replace based on eslint vs. prettier decisions!", - "generate:icons": "svgr --config-file svgr.config.js --no-prettier --out-dir src/icons/generated-icons ../icons/assets", - "test": "echo \"No tests yet\"", - "clean": "rm -rf dist src/icons/generated-icons" - }, - "peerDependencies": { - "react": ">=16.8.0" - }, - "dependencies": { - "@videojs/core-preview": "workspace:*", - "@videojs/icons-preview": "workspace:*", - "@videojs/utils-preview": "workspace:*" - }, - "devDependencies": { - "@svgr/babel-plugin-add-jsx-attribute": "^8.0.0", - "@svgr/cli": "^8.1.0", - "@svgr/core": "^8.1.0", - "@svgr/plugin-jsx": "^8.1.0", - "@tailwindcss/cli": "^4.1.17", - "@types/postcss-prefix-selector": "^1.16.3", - "@types/react": "^18.0.0", - "postcss": "^8.4.49", - "postcss-prefix-selector": "^1.16.1", - "react": "^18.0.0", - "tailwindcss": "^4.1.17", - "tsdown": "^0.15.9", - "typescript": "^5.9.3" - }, - "publishConfig": { - "access": "public" - } -} diff --git a/packages/__tech-preview__/react/src/components/CurrentTimeDisplay.tsx b/packages/__tech-preview__/react/src/components/CurrentTimeDisplay.tsx deleted file mode 100644 index f8aeb029..00000000 --- a/packages/__tech-preview__/react/src/components/CurrentTimeDisplay.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import type { PropsWithChildren } from 'react'; -import type { ConnectedComponent } from '../utils/component-factory'; - -import { currentTimeDisplayStateDefinition } from '@videojs/core-preview/store'; -import { formatDisplayTime, shallowEqual } from '@videojs/utils-preview'; - -import { useMediaSelector } from '@/store'; -import { toConnectedComponent } from '../utils/component-factory'; - -export function useCurrentTimeDisplayState(_props?: any): { - currentTime: number; - duration: number; -} { - /** @TODO Fix type issues with hooks (CJP) */ - const mediaState = useMediaSelector(currentTimeDisplayStateDefinition.stateTransform, shallowEqual); - - // Current time display is read-only, no request methods needed - return { - currentTime: mediaState.currentTime ?? 0, - duration: mediaState.duration ?? 0, - }; -} - -export type CurrentTimeDisplayState = ReturnType; - -export interface CurrentTimeDisplayProps extends React.ComponentProps<'span'> { - showRemaining?: boolean; -} - -export function useCurrentTimeDisplayProps( - props: PropsWithChildren, - _state: ReturnType, -): PropsWithChildren> { - const baseProps: Record = { - /** external props spread last to allow for overriding */ - ...props, - }; - - return baseProps; -} - -export function renderCurrentTimeDisplay(props: CurrentTimeDisplayProps, state: CurrentTimeDisplayState): JSX.Element { - const { showRemaining, ...restProps } = props; - - /** @TODO Should this live here or elsewhere? (CJP) */ - const timeLabel - = showRemaining && state.duration != null && state.currentTime != null - ? formatDisplayTime(-(state.duration - state.currentTime)) - : formatDisplayTime(state.currentTime); - - return {timeLabel}; -} - -export const CurrentTimeDisplay: ConnectedComponent - = toConnectedComponent( - useCurrentTimeDisplayState, - useCurrentTimeDisplayProps, - renderCurrentTimeDisplay, - 'CurrentTimeDisplay', - ); - -export default CurrentTimeDisplay; diff --git a/packages/__tech-preview__/react/src/components/DurationDisplay.tsx b/packages/__tech-preview__/react/src/components/DurationDisplay.tsx deleted file mode 100644 index d1fc90ab..00000000 --- a/packages/__tech-preview__/react/src/components/DurationDisplay.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import type { PropsWithChildren } from 'react'; -import type { ConnectedComponent } from '../utils/component-factory'; - -import { durationDisplayStateDefinition } from '@videojs/core-preview/store'; -import { formatDisplayTime, shallowEqual } from '@videojs/utils-preview'; - -import { useMediaSelector } from '@/store'; -import { toConnectedComponent } from '../utils/component-factory'; - -export function useDurationDisplayState(_props: any): { - duration: number; -} { - /** @TODO Fix type issues with hooks (CJP) */ - const mediaState = useMediaSelector(durationDisplayStateDefinition.stateTransform, shallowEqual); - - // Duration display is read-only, no request methods needed - return { - duration: mediaState.duration ?? 0, - }; -} - -export type useDurationDisplayState = typeof useDurationDisplayState; -export type DurationDisplayState = ReturnType; - -export function useDurationDisplayProps(props: PropsWithChildren): PropsWithChildren> { - const baseProps: Record = { - /** external props spread last to allow for overriding */ - ...props, - }; - - return baseProps; -} - -export type useDurationDisplayProps = typeof useDurationDisplayProps; -type DurationDisplayProps = ReturnType; - -export function renderDurationDisplay(props: DurationDisplayProps, state: DurationDisplayState): JSX.Element { - return {formatDisplayTime(state.duration)}; -} - -export type renderDurationDisplay = typeof renderDurationDisplay; - -export const DurationDisplay: ConnectedComponent - = toConnectedComponent(useDurationDisplayState, useDurationDisplayProps, renderDurationDisplay, 'DurationDisplay'); - -export default DurationDisplay; diff --git a/packages/__tech-preview__/react/src/components/FullscreenButton.tsx b/packages/__tech-preview__/react/src/components/FullscreenButton.tsx deleted file mode 100644 index c58f7d1e..00000000 --- a/packages/__tech-preview__/react/src/components/FullscreenButton.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import type { PropsWithChildren } from 'react'; -import type { ConnectedComponent } from '../utils/component-factory'; - -import { fullscreenButtonStateDefinition } from '@videojs/core-preview/store'; - -import { shallowEqual } from '@videojs/utils-preview'; -import { useMemo } from 'react'; - -import { useMediaSelector, useMediaStore } from '@/store'; -import { toConnectedComponent } from '../utils/component-factory'; - -export function useFullscreenButtonState(_props?: any): { - fullscreen: boolean; - requestEnterFullscreen: () => void; - requestExitFullscreen: () => void; -} { - const mediaStore = useMediaStore(); - const mediaState = useMediaSelector(fullscreenButtonStateDefinition.stateTransform, shallowEqual); - const methods = useMemo( - () => fullscreenButtonStateDefinition.createRequestMethods(mediaStore.dispatch), - [mediaStore], - ); - - return { - fullscreen: mediaState.fullscreen, - requestEnterFullscreen: methods.requestEnterFullscreen, - requestExitFullscreen: methods.requestExitFullscreen, - } as const; -} - -export type FullscreenButtonState = ReturnType; - -export function getFullscreenButtonProps( - props: PropsWithChildren, - state: FullscreenButtonState, -): PropsWithChildren> { - const baseProps: Record = { - /** @TODO Need another state provider in core for i18n (CJP) */ - /** aria attributes/props */ - role: 'button', - 'aria-label': state.fullscreen ? 'exit fullscreen' : 'enter fullscreen', - /** tooltip */ - 'data-tooltip': state.fullscreen ? 'Exit Fullscreen' : 'Enter Fullscreen', - 'data-button': 'fullscreen', - /** external props spread last to allow for overriding */ - ...props, - }; - - // Handle boolean data attribute: present with empty string when true, absent when false - if (state.fullscreen) { - baseProps['data-fullscreen'] = ''; - } - - return baseProps; -} - -export type FullscreenButtonProps = ReturnType; - -export function renderFullscreenButton(props: FullscreenButtonProps, state: FullscreenButtonState): JSX.Element { - return ( - - ); -} - -export const FullscreenButton: ConnectedComponent - = toConnectedComponent(useFullscreenButtonState, getFullscreenButtonProps, renderFullscreenButton, 'FullscreenButton'); - -export default FullscreenButton; diff --git a/packages/__tech-preview__/react/src/components/HlsVideo.tsx b/packages/__tech-preview__/react/src/components/HlsVideo.tsx deleted file mode 100644 index 00d8a264..00000000 --- a/packages/__tech-preview__/react/src/components/HlsVideo.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import type { - CSSProperties, - DetailedHTMLProps, - ElementType, - PropsWithChildren, - Ref, - VideoHTMLAttributes, -} from 'react'; - -import { createMediaPlaybackController } from '@videojs/core-preview/media'; - -import { forwardRef, useImperativeHandle, useRef } from 'react'; -import { useMediaRef } from '@/store'; - -export interface MuxVideoProps { - 'playback-id'?: string; -} - -type MediaStateOwner = NonNullable>[0]>; - -/** @TODO Improve type inference and narrowing/widening for different use cases (CJP) */ -type ComponentType = ElementType< - Omit, HTMLVideoElement>, 'ref'> & { - ref: Ref; - } ->; - -// These are the first steps/WIP POC of decoupling the Media State Owner from the DOM. -// Note that everything will still work if you use: -// 1. an audio/video element directly -// 2. a custom element a la media-elements -type CreateMediaStateOwner = typeof createMediaPlaybackController; - -function useMediaStateOwner(ref: Ref, createMediaPlaybackController: CreateMediaStateOwner) { - const mediaStateOwnerRef = useRef(createMediaPlaybackController(/* props? */)); - useImperativeHandle(ref, () => mediaStateOwnerRef.current, []); - /** @TODO Parameterize this (CJP) */ - type ComponentProps = DetailedHTMLProps, HTMLVideoElement>; - return { - updateMediaElement(mediaEl: HTMLMediaElement | null, props: ComponentProps) { - // NOTE: The details here will almost definitely change for a less "bare bones"/"POC" implementation of Media State Owner impl. (CJP) - mediaStateOwnerRef.current.mediaElement = mediaEl ?? undefined; - mediaStateOwnerRef.current.src = props.src as string; - if (props.muted) { - mediaStateOwnerRef.current.muted = props.muted; - } - }, - }; -} - -const DefaultVideoComponent: ElementType< - Omit, HTMLVideoElement>, 'ref'> & { ref: Ref } -> = forwardRef(({ children, ...props }, ref) => { - const { updateMediaElement } = useMediaStateOwner(ref, createMediaPlaybackController); - return ( - - ); -}); - -/** - * @description This is a "thin wrapper" around the media component whose primary responsibility is to wire up the element - * to the 's MediaStore for the media state. - * @param props - Identical to both a