From 53db821cfc5c003dc2ef7c52095c20fb0eb18f36 Mon Sep 17 00:00:00 2001 From: Kian Bazarjani Date: Thu, 21 May 2026 19:35:46 -0400 Subject: [PATCH] fix(video-player): compose root event handlers --- .changeset/clean-owls-merge.md | 5 ++++ packages/react/src/utils/types.ts | 23 +++++++++++++++++++ .../src/video-player/components/root/root.tsx | 17 ++++++++++---- packages/react/src/video-player/types.ts | 4 +++- 4 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 .changeset/clean-owls-merge.md diff --git a/.changeset/clean-owls-merge.md b/.changeset/clean-owls-merge.md new file mode 100644 index 00000000..4820a993 --- /dev/null +++ b/.changeset/clean-owls-merge.md @@ -0,0 +1,5 @@ +--- +"@bazza-ui/react": patch +--- + +Compose video player root event handlers so consumer handlers run before internal behavior and can opt out with preventBaseUIHandler. diff --git a/packages/react/src/utils/types.ts b/packages/react/src/utils/types.ts index f65ff51f..b0d5973e 100644 --- a/packages/react/src/utils/types.ts +++ b/packages/react/src/utils/types.ts @@ -15,6 +15,29 @@ export type HTMLProps = React.HTMLAttributes & { ref?: React.Ref | undefined } +/** + * React synthetic event augmented by Base UI's mergeProps utility. + */ +export type PreventableBaseHandlerEvent< + E extends React.SyntheticEvent, +> = E & { + preventBaseUIHandler: () => void + readonly baseUIHandlerPrevented?: boolean +} + +type WithPreventableBaseHandler = T extends (event: infer E) => infer R + ? E extends React.SyntheticEvent + ? (event: PreventableBaseHandlerEvent) => R + : T + : T + +/** + * Adds Base UI's preventBaseUIHandler event typing to React event handlers. + */ +export type WithPreventableBaseHandlers = { + [K in keyof T]: WithPreventableBaseHandler +} + /** * Props shared by all Bazza UI components. * Includes className (string or state-based function), render prop, and style (object or state-based function). diff --git a/packages/react/src/video-player/components/root/root.tsx b/packages/react/src/video-player/components/root/root.tsx index 3cd77e79..f266bce6 100644 --- a/packages/react/src/video-player/components/root/root.tsx +++ b/packages/react/src/video-player/components/root/root.tsx @@ -1,5 +1,6 @@ 'use client' +import { mergeProps } from '@base-ui/react/merge-props' import * as React from 'react' import { VideoPlayerContext } from '../../contexts/video-player-context.js' import { useKeyboardShortcuts } from '../../hooks/use-keyboard-shortcuts.js' @@ -966,6 +967,16 @@ const VideoPlayerRootImpl = React.forwardRef< context.resetIdle() }, [context]) + const rootProps = mergeProps<'div'>( + { + onMouseMove: handleMouseMove, + onMouseEnter: handleMouseEnter, + onMouseLeave: handleMouseLeave, + onClick: handleClick, + }, + divProps, + ) + // Data attributes for styling const dataAttributes = { [RootDataAttributes.playing]: context.playing || undefined, @@ -992,17 +1003,13 @@ const VideoPlayerRootImpl = React.forwardRef< return ( // biome-ignore lint/a11y/noStaticElementInteractions: allowed
{/* Inject scoped style to hide cursor - uses !important to override child styles */} {shouldHideCursor && ( diff --git a/packages/react/src/video-player/types.ts b/packages/react/src/video-player/types.ts index 590e7454..b8cd6904 100644 --- a/packages/react/src/video-player/types.ts +++ b/packages/react/src/video-player/types.ts @@ -1,5 +1,7 @@ import type * as React from 'react' +import type { WithPreventableBaseHandlers } from '../utils/types.js' + // ============================================================================ // Playback Status & Intent // ============================================================================ @@ -216,7 +218,7 @@ export interface VideoPlayerExternalActions { // Omit conflicting native event handlers type DivPropsWithoutConflicts = Omit< - React.ComponentPropsWithRef<'div'>, + WithPreventableBaseHandlers>, 'onVolumeChange' | 'onEnded' | 'onWaiting' | 'onSeeking' | 'onSeeked' >