From 13e9658d9deebad5f54e9881f5c652ea60a977b4 Mon Sep 17 00:00:00 2001 From: atomiks Date: Fri, 20 Dec 2024 19:54:18 +1100 Subject: [PATCH 01/10] [popups] Apply `aria-hidden` to `Arrow` parts (#1196) --- packages/react/src/menu/arrow/MenuArrow.tsx | 14 +++++-- packages/react/src/menu/arrow/useMenuArrow.ts | 34 ----------------- .../react/src/popover/arrow/PopoverArrow.tsx | 14 +++++-- .../src/popover/arrow/usePopoverArrow.ts | 33 ----------------- .../preview-card/arrow/PreviewCardArrow.tsx | 13 +++++-- .../preview-card/arrow/usePreviewCardArrow.ts | 37 ------------------- .../react/src/select/arrow/SelectArrow.tsx | 1 + .../react/src/tooltip/arrow/TooltipArrow.tsx | 14 +++++-- .../src/tooltip/arrow/useTooltipArrow.ts | 33 ----------------- 9 files changed, 40 insertions(+), 153 deletions(-) delete mode 100644 packages/react/src/menu/arrow/useMenuArrow.ts delete mode 100644 packages/react/src/popover/arrow/usePopoverArrow.ts delete mode 100644 packages/react/src/preview-card/arrow/usePreviewCardArrow.ts delete mode 100644 packages/react/src/tooltip/arrow/useTooltipArrow.ts diff --git a/packages/react/src/menu/arrow/MenuArrow.tsx b/packages/react/src/menu/arrow/MenuArrow.tsx index a608447b90..e75cd61a03 100644 --- a/packages/react/src/menu/arrow/MenuArrow.tsx +++ b/packages/react/src/menu/arrow/MenuArrow.tsx @@ -1,7 +1,6 @@ 'use client'; import * as React from 'react'; import PropTypes from 'prop-types'; -import { useMenuArrow } from './useMenuArrow'; import { useMenuPositionerContext } from '../positioner/MenuPositionerContext'; import { useMenuRootContext } from '../root/MenuRootContext'; import { useComponentRenderer } from '../../utils/useComponentRenderer'; @@ -9,6 +8,7 @@ import { useForkRef } from '../../utils/useForkRef'; import type { Side, Align } from '../../utils/useAnchorPositioning'; import type { BaseUIComponentProps } from '../../utils/types'; import { popupStateMapping } from '../../utils/popupStateMapping'; +import { mergeReactProps } from '../../utils/mergeReactProps'; /** * Displays an element positioned against the menu anchor. @@ -25,9 +25,15 @@ const MenuArrow = React.forwardRef(function MenuArrow( const { open } = useMenuRootContext(); const { arrowRef, side, align, arrowUncentered, arrowStyles } = useMenuPositionerContext(); - const { getArrowProps } = useMenuArrow({ - arrowStyles, - }); + const getArrowProps = React.useCallback( + (externalProps = {}) => { + return mergeReactProps<'div'>(externalProps, { + style: arrowStyles, + 'aria-hidden': true, + }); + }, + [arrowStyles], + ); const state: MenuArrow.State = React.useMemo( () => ({ diff --git a/packages/react/src/menu/arrow/useMenuArrow.ts b/packages/react/src/menu/arrow/useMenuArrow.ts deleted file mode 100644 index c0b1d30ed9..0000000000 --- a/packages/react/src/menu/arrow/useMenuArrow.ts +++ /dev/null @@ -1,34 +0,0 @@ -'use client'; -import * as React from 'react'; -import { mergeReactProps } from '../../utils/mergeReactProps'; -import type { GenericHTMLProps } from '../../utils/types'; - -export function useMenuArrow(params: useMenuArrow.Parameters): useMenuArrow.ReturnValue { - const { arrowStyles } = params; - - const getArrowProps = React.useCallback( - (externalProps = {}) => { - return mergeReactProps<'div'>(externalProps, { - style: arrowStyles, - }); - }, - [arrowStyles], - ); - - return React.useMemo( - () => ({ - getArrowProps, - }), - [getArrowProps], - ); -} - -export namespace useMenuArrow { - export interface Parameters { - arrowStyles: React.CSSProperties; - } - - export interface ReturnValue { - getArrowProps: (externalProps?: GenericHTMLProps) => GenericHTMLProps; - } -} diff --git a/packages/react/src/popover/arrow/PopoverArrow.tsx b/packages/react/src/popover/arrow/PopoverArrow.tsx index 40caaf0192..9507aaf8d5 100644 --- a/packages/react/src/popover/arrow/PopoverArrow.tsx +++ b/packages/react/src/popover/arrow/PopoverArrow.tsx @@ -5,10 +5,10 @@ import { usePopoverPositionerContext } from '../positioner/PopoverPositionerCont import { usePopoverRootContext } from '../root/PopoverRootContext'; import { useComponentRenderer } from '../../utils/useComponentRenderer'; import { useForkRef } from '../../utils/useForkRef'; -import { usePopoverArrow } from './usePopoverArrow'; import type { Align, Side } from '../../utils/useAnchorPositioning'; import type { BaseUIComponentProps } from '../../utils/types'; import { popupStateMapping } from '../../utils/popupStateMapping'; +import { mergeReactProps } from '../../utils/mergeReactProps'; /** * Displays an element positioned against the popover anchor. @@ -25,9 +25,15 @@ const PopoverArrow = React.forwardRef(function PopoverArrow( const { open } = usePopoverRootContext(); const { arrowRef, side, align, arrowUncentered, arrowStyles } = usePopoverPositionerContext(); - const { getArrowProps } = usePopoverArrow({ - arrowStyles, - }); + const getArrowProps = React.useCallback( + (externalProps = {}) => { + return mergeReactProps<'div'>(externalProps, { + style: arrowStyles, + 'aria-hidden': true, + }); + }, + [arrowStyles], + ); const state: PopoverArrow.State = React.useMemo( () => ({ diff --git a/packages/react/src/popover/arrow/usePopoverArrow.ts b/packages/react/src/popover/arrow/usePopoverArrow.ts deleted file mode 100644 index 740a1755fb..0000000000 --- a/packages/react/src/popover/arrow/usePopoverArrow.ts +++ /dev/null @@ -1,33 +0,0 @@ -import * as React from 'react'; -import { mergeReactProps } from '../../utils/mergeReactProps'; -import type { GenericHTMLProps } from '../../utils/types'; - -export function usePopoverArrow(params: usePopoverArrow.Parameters): usePopoverArrow.ReturnValue { - const { arrowStyles } = params; - - const getArrowProps = React.useCallback( - (externalProps = {}) => { - return mergeReactProps<'div'>(externalProps, { - style: arrowStyles, - }); - }, - [arrowStyles], - ); - - return React.useMemo( - () => ({ - getArrowProps, - }), - [getArrowProps], - ); -} - -namespace usePopoverArrow { - export interface Parameters { - arrowStyles: React.CSSProperties; - } - - export interface ReturnValue { - getArrowProps: (externalProps?: GenericHTMLProps) => GenericHTMLProps; - } -} diff --git a/packages/react/src/preview-card/arrow/PreviewCardArrow.tsx b/packages/react/src/preview-card/arrow/PreviewCardArrow.tsx index 08695fa4f5..204571c830 100644 --- a/packages/react/src/preview-card/arrow/PreviewCardArrow.tsx +++ b/packages/react/src/preview-card/arrow/PreviewCardArrow.tsx @@ -3,12 +3,12 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { useComponentRenderer } from '../../utils/useComponentRenderer'; import { usePreviewCardPositionerContext } from '../positioner/PreviewCardPositionerContext'; -import { usePreviewCardArrow } from './usePreviewCardArrow'; import { useForkRef } from '../../utils/useForkRef'; import { usePreviewCardRootContext } from '../root/PreviewCardContext'; import type { BaseUIComponentProps } from '../../utils/types'; import type { Align, Side } from '../../utils/useAnchorPositioning'; import { popupStateMapping } from '../../utils/popupStateMapping'; +import { mergeReactProps } from '../../utils/mergeReactProps'; /** * Displays an element positioned against the preview card anchor. @@ -25,9 +25,14 @@ const PreviewCardArrow = React.forwardRef(function PreviewCardArrow( const { open } = usePreviewCardRootContext(); const { arrowRef, side, align, arrowUncentered, arrowStyles } = usePreviewCardPositionerContext(); - const { getArrowProps } = usePreviewCardArrow({ - arrowStyles, - }); + const getArrowProps = React.useCallback( + (externalProps = {}) => + mergeReactProps<'div'>(externalProps, { + style: arrowStyles, + 'aria-hidden': true, + }), + [arrowStyles], + ); const state: PreviewCardArrow.State = React.useMemo( () => ({ diff --git a/packages/react/src/preview-card/arrow/usePreviewCardArrow.ts b/packages/react/src/preview-card/arrow/usePreviewCardArrow.ts deleted file mode 100644 index 6080cfeea8..0000000000 --- a/packages/react/src/preview-card/arrow/usePreviewCardArrow.ts +++ /dev/null @@ -1,37 +0,0 @@ -import * as React from 'react'; -import { mergeReactProps } from '../../utils/mergeReactProps'; - -export function usePreviewCardArrow( - params: usePreviewCardArrow.Parameters, -): usePreviewCardArrow.ReturnValue { - const { arrowStyles } = params; - - const getArrowProps = React.useCallback( - (externalProps = {}) => { - return mergeReactProps<'div'>(externalProps, { - style: arrowStyles, - }); - }, - [arrowStyles], - ); - - return React.useMemo( - () => ({ - getArrowProps, - }), - [getArrowProps], - ); -} - -namespace usePreviewCardArrow { - export interface Parameters { - arrowStyles: React.CSSProperties; - hidden?: boolean; - } - - export interface ReturnValue { - getArrowProps: ( - externalProps?: React.HTMLAttributes, - ) => React.HTMLAttributes; - } -} diff --git a/packages/react/src/select/arrow/SelectArrow.tsx b/packages/react/src/select/arrow/SelectArrow.tsx index 499b113a66..eaf588b309 100644 --- a/packages/react/src/select/arrow/SelectArrow.tsx +++ b/packages/react/src/select/arrow/SelectArrow.tsx @@ -29,6 +29,7 @@ const SelectArrow = React.forwardRef(function SelectArrow( (externalProps = {}) => mergeReactProps<'div'>(externalProps, { style: arrowStyles, + 'aria-hidden': true, }), [arrowStyles], ); diff --git a/packages/react/src/tooltip/arrow/TooltipArrow.tsx b/packages/react/src/tooltip/arrow/TooltipArrow.tsx index c3064cb752..28e625a116 100644 --- a/packages/react/src/tooltip/arrow/TooltipArrow.tsx +++ b/packages/react/src/tooltip/arrow/TooltipArrow.tsx @@ -4,10 +4,10 @@ import PropTypes from 'prop-types'; import { useComponentRenderer } from '../../utils/useComponentRenderer'; import { useForkRef } from '../../utils/useForkRef'; import { useTooltipPositionerContext } from '../positioner/TooltipPositionerContext'; -import { useTooltipArrow } from './useTooltipArrow'; import type { BaseUIComponentProps } from '../../utils/types'; import type { Side, Align } from '../../utils/useAnchorPositioning'; import { popupStateMapping } from '../../utils/popupStateMapping'; +import { mergeReactProps } from '../../utils/mergeReactProps'; /** * Displays an element positioned against the tooltip anchor. @@ -24,9 +24,15 @@ const TooltipArrow = React.forwardRef(function TooltipArrow( const { open, arrowRef, side, align, arrowUncentered, arrowStyles } = useTooltipPositionerContext(); - const { getArrowProps } = useTooltipArrow({ - arrowStyles, - }); + const getArrowProps = React.useCallback( + (externalProps = {}) => { + return mergeReactProps<'div'>(externalProps, { + style: arrowStyles, + 'aria-hidden': true, + }); + }, + [arrowStyles], + ); const state: TooltipArrow.State = React.useMemo( () => ({ diff --git a/packages/react/src/tooltip/arrow/useTooltipArrow.ts b/packages/react/src/tooltip/arrow/useTooltipArrow.ts deleted file mode 100644 index f1fcd3529c..0000000000 --- a/packages/react/src/tooltip/arrow/useTooltipArrow.ts +++ /dev/null @@ -1,33 +0,0 @@ -import * as React from 'react'; -import { mergeReactProps } from '../../utils/mergeReactProps'; -import type { GenericHTMLProps } from '../../utils/types'; - -export function useTooltipArrow(params: useTooltipArrow.Parameters): useTooltipArrow.ReturnValue { - const { arrowStyles } = params; - - const getArrowProps = React.useCallback( - (externalProps = {}) => { - return mergeReactProps<'div'>(externalProps, { - style: arrowStyles, - }); - }, - [arrowStyles], - ); - - return React.useMemo( - () => ({ - getArrowProps, - }), - [getArrowProps], - ); -} - -namespace useTooltipArrow { - export interface Parameters { - arrowStyles: React.CSSProperties; - } - - export interface ReturnValue { - getArrowProps: (props?: GenericHTMLProps) => GenericHTMLProps; - } -} From 9dfbc24938dc9aea72b041c5616870a4cead8a54 Mon Sep 17 00:00:00 2001 From: atomiks Date: Fri, 20 Dec 2024 20:14:51 +1100 Subject: [PATCH 02/10] [ScrollArea] Differentiate `x`/`y` orientation `data-scrolling` (#1188) --- .../scroll-area/root/ScrollAreaRootContext.ts | 8 ++- .../src/scroll-area/root/useScrollAreaRoot.ts | 68 +++++++++++++------ .../scrollbar/ScrollAreaScrollbar.test.tsx | 29 ++++++-- .../scrollbar/ScrollAreaScrollbar.tsx | 9 ++- .../src/scroll-area/thumb/ScrollAreaThumb.tsx | 10 ++- .../viewport/useScrollAreaViewport.tsx | 11 ++- 6 files changed, 102 insertions(+), 33 deletions(-) diff --git a/packages/react/src/scroll-area/root/ScrollAreaRootContext.ts b/packages/react/src/scroll-area/root/ScrollAreaRootContext.ts index f9d26a558f..2e7fb8f6ad 100644 --- a/packages/react/src/scroll-area/root/ScrollAreaRootContext.ts +++ b/packages/react/src/scroll-area/root/ScrollAreaRootContext.ts @@ -9,8 +9,10 @@ export interface ScrollAreaRootContext { touchModality: boolean; hovering: boolean; setHovering: React.Dispatch>; - scrolling: boolean; - setScrolling: React.Dispatch>; + scrollingX: boolean; + setScrollingX: React.Dispatch>; + scrollingY: boolean; + setScrollingY: React.Dispatch>; viewportRef: React.RefObject; scrollbarYRef: React.RefObject; thumbYRef: React.RefObject; @@ -20,7 +22,7 @@ export interface ScrollAreaRootContext { handlePointerDown: (event: React.PointerEvent) => void; handlePointerMove: (event: React.PointerEvent) => void; handlePointerUp: (event: React.PointerEvent) => void; - handleScroll: () => void; + handleScroll: (scrollPosition: { x: number; y: number }) => void; rootId: string | undefined; hiddenState: { scrollbarYHidden: boolean; diff --git a/packages/react/src/scroll-area/root/useScrollAreaRoot.ts b/packages/react/src/scroll-area/root/useScrollAreaRoot.ts index e1edda89b3..05b818fdb8 100644 --- a/packages/react/src/scroll-area/root/useScrollAreaRoot.ts +++ b/packages/react/src/scroll-area/root/useScrollAreaRoot.ts @@ -16,7 +16,8 @@ export function useScrollAreaRoot(params: useScrollAreaRoot.Parameters) { const { dir: dirParam } = params; const [hovering, setHovering] = React.useState(false); - const [scrolling, setScrolling] = React.useState(false); + const [scrollingX, setScrollingX] = React.useState(false); + const [scrollingY, setScrollingY] = React.useState(false); const [cornerSize, setCornerSize] = React.useState({ width: 0, height: 0 }); const [thumbSize, setThumbSize] = React.useState({ width: 0, height: 0 }); const [touchModality, setTouchModality] = React.useState(false); @@ -36,7 +37,9 @@ export function useScrollAreaRoot(params: useScrollAreaRoot.Parameters) { const startScrollTopRef = React.useRef(0); const startScrollLeftRef = React.useRef(0); const currentOrientationRef = React.useRef<'vertical' | 'horizontal'>('vertical'); - const timeoutRef = React.useRef(-1); + const scrollYTimeoutRef = React.useRef(-1); + const scrollXTimeoutRef = React.useRef(-1); + const scrollPositionRef = React.useRef({ x: 0, y: 0 }); const [hiddenState, setHiddenState] = React.useState({ scrollbarYHidden: false, @@ -55,17 +58,33 @@ export function useScrollAreaRoot(params: useScrollAreaRoot.Parameters) { React.useEffect(() => { return () => { - window.clearTimeout(timeoutRef.current); + window.clearTimeout(scrollYTimeoutRef.current); + window.clearTimeout(scrollXTimeoutRef.current); }; }, []); - const handleScroll = useEventCallback(() => { - setScrolling(true); + const handleScroll = useEventCallback((scrollPosition: { x: number; y: number }) => { + const offsetX = scrollPosition.x - scrollPositionRef.current.x; + const offsetY = scrollPosition.y - scrollPositionRef.current.y; + scrollPositionRef.current = scrollPosition; - window.clearTimeout(timeoutRef.current); - timeoutRef.current = window.setTimeout(() => { - setScrolling(false); - }, SCROLL_TIMEOUT); + if (offsetY !== 0) { + setScrollingY(true); + + window.clearTimeout(scrollYTimeoutRef.current); + scrollYTimeoutRef.current = window.setTimeout(() => { + setScrollingY(false); + }, SCROLL_TIMEOUT); + } + + if (offsetX !== 0) { + setScrollingX(true); + + window.clearTimeout(scrollXTimeoutRef.current); + scrollXTimeoutRef.current = window.setTimeout(() => { + setScrollingX(false); + }, SCROLL_TIMEOUT); + } }); const handlePointerDown = useEventCallback((event: React.PointerEvent) => { @@ -116,10 +135,12 @@ export function useScrollAreaRoot(params: useScrollAreaRoot.Parameters) { viewportRef.current.scrollTop = startScrollTopRef.current + scrollRatioY * (scrollableContentHeight - viewportHeight); event.preventDefault(); - setScrolling(true); - window.clearTimeout(timeoutRef.current); - timeoutRef.current = window.setTimeout(() => { - setScrolling(false); + + setScrollingY(true); + + window.clearTimeout(scrollYTimeoutRef.current); + scrollYTimeoutRef.current = window.setTimeout(() => { + setScrollingY(false); }, SCROLL_TIMEOUT); } @@ -137,10 +158,12 @@ export function useScrollAreaRoot(params: useScrollAreaRoot.Parameters) { viewportRef.current.scrollLeft = startScrollLeftRef.current + scrollRatioX * (scrollableContentWidth - viewportWidth); event.preventDefault(); - setScrolling(true); - window.clearTimeout(timeoutRef.current); - timeoutRef.current = window.setTimeout(() => { - setScrolling(false); + + setScrollingX(true); + + window.clearTimeout(scrollXTimeoutRef.current); + scrollXTimeoutRef.current = window.setTimeout(() => { + setScrollingX(false); }, SCROLL_TIMEOUT); } } @@ -201,8 +224,10 @@ export function useScrollAreaRoot(params: useScrollAreaRoot.Parameters) { setThumbSize, touchModality, cornerRef, - scrolling, - setScrolling, + scrollingX, + setScrollingX, + scrollingY, + setScrollingY, hovering, setHovering, viewportRef, @@ -224,7 +249,10 @@ export function useScrollAreaRoot(params: useScrollAreaRoot.Parameters) { thumbSize, touchModality, cornerRef, - scrolling, + scrollingX, + setScrollingX, + scrollingY, + setScrollingY, hovering, setHovering, viewportRef, diff --git a/packages/react/src/scroll-area/scrollbar/ScrollAreaScrollbar.test.tsx b/packages/react/src/scroll-area/scrollbar/ScrollAreaScrollbar.test.tsx index fde46c87ff..03a497a1dc 100644 --- a/packages/react/src/scroll-area/scrollbar/ScrollAreaScrollbar.test.tsx +++ b/packages/react/src/scroll-area/scrollbar/ScrollAreaScrollbar.test.tsx @@ -18,7 +18,7 @@ describe('', () => { }, })); - it('adds [data-scrolling] attribute when viewport is scrolled', async () => { + it('adds [data-scrolling] attribute when viewport is scrolled in the correct direction', async () => { await render( @@ -32,19 +32,40 @@ describe('', () => { const verticalScrollbar = screen.getByTestId('vertical'); const horizontalScrollbar = screen.getByTestId('horizontal'); + const viewport = screen.getByTestId('viewport'); expect(verticalScrollbar).not.to.have.attribute('data-scrolling'); expect(horizontalScrollbar).not.to.have.attribute('data-scrolling'); - fireEvent.scroll(screen.getByTestId('viewport')); + fireEvent.scroll(viewport, { + target: { + scrollTop: 1, + }, + }); expect(verticalScrollbar).to.have.attribute('data-scrolling', ''); - expect(horizontalScrollbar).to.have.attribute('data-scrolling', ''); + expect(horizontalScrollbar).not.to.have.attribute('data-scrolling', ''); clock.tick(SCROLL_TIMEOUT - 1); expect(verticalScrollbar).to.have.attribute('data-scrolling', ''); - expect(horizontalScrollbar).to.have.attribute('data-scrolling', ''); + expect(horizontalScrollbar).not.to.have.attribute('data-scrolling', ''); + + fireEvent.scroll(viewport, { + target: { + scrollLeft: 1, + }, + }); + + clock.tick(1); // vertical just finished + + expect(verticalScrollbar).not.to.have.attribute('data-scrolling'); + expect(horizontalScrollbar).to.have.attribute('data-scrolling'); + + clock.tick(SCROLL_TIMEOUT - 2); // already ticked 1ms above + + expect(verticalScrollbar).not.to.have.attribute('data-scrolling'); + expect(horizontalScrollbar).to.have.attribute('data-scrolling'); clock.tick(1); diff --git a/packages/react/src/scroll-area/scrollbar/ScrollAreaScrollbar.tsx b/packages/react/src/scroll-area/scrollbar/ScrollAreaScrollbar.tsx index cc217e55fd..0f2d67131f 100644 --- a/packages/react/src/scroll-area/scrollbar/ScrollAreaScrollbar.tsx +++ b/packages/react/src/scroll-area/scrollbar/ScrollAreaScrollbar.tsx @@ -20,7 +20,7 @@ const ScrollAreaScrollbar = React.forwardRef(function ScrollAreaScrollbar( ) { const { render, className, orientation = 'vertical', keepMounted = false, ...otherProps } = props; - const { hovering, scrolling, hiddenState, scrollbarYRef, scrollbarXRef } = + const { hovering, scrollingX, scrollingY, hiddenState, scrollbarYRef, scrollbarXRef } = useScrollAreaRootContext(); const mergedRef = useForkRef( @@ -31,10 +31,13 @@ const ScrollAreaScrollbar = React.forwardRef(function ScrollAreaScrollbar( const state: ScrollAreaScrollbar.State = React.useMemo( () => ({ hovering, - scrolling, + scrolling: { + horizontal: scrollingX, + vertical: scrollingY, + }[orientation], orientation, }), - [hovering, scrolling, orientation], + [hovering, scrollingX, scrollingY, orientation], ); const { getScrollbarProps } = useScrollAreaScrollbar({ diff --git a/packages/react/src/scroll-area/thumb/ScrollAreaThumb.tsx b/packages/react/src/scroll-area/thumb/ScrollAreaThumb.tsx index c87d8189cc..ad7c933faa 100644 --- a/packages/react/src/scroll-area/thumb/ScrollAreaThumb.tsx +++ b/packages/react/src/scroll-area/thumb/ScrollAreaThumb.tsx @@ -27,7 +27,8 @@ const ScrollAreaThumb = React.forwardRef(function ScrollAreaThumb( handlePointerDown, handlePointerMove, handlePointerUp, - setScrolling, + setScrollingX, + setScrollingY, } = useScrollAreaRootContext(); const { orientation } = useScrollAreaScrollbarContext(); @@ -45,7 +46,12 @@ const ScrollAreaThumb = React.forwardRef(function ScrollAreaThumb( onPointerDown: handlePointerDown, onPointerMove: handlePointerMove, onPointerUp(event) { - setScrolling(false); + if (orientation === 'vertical') { + setScrollingY(false); + } + if (orientation === 'horizontal') { + setScrollingX(false); + } handlePointerUp(event); }, style: { diff --git a/packages/react/src/scroll-area/viewport/useScrollAreaViewport.tsx b/packages/react/src/scroll-area/viewport/useScrollAreaViewport.tsx index ed3f242e12..92fb1fa479 100644 --- a/packages/react/src/scroll-area/viewport/useScrollAreaViewport.tsx +++ b/packages/react/src/scroll-area/viewport/useScrollAreaViewport.tsx @@ -184,8 +184,16 @@ export function useScrollAreaViewport(params: useScrollAreaViewport.Parameters) overflow: 'scroll', }, onScroll() { + if (!viewportRef.current) { + return; + } + computeThumb(); - handleScroll(); + + handleScroll({ + x: viewportRef.current.scrollLeft, + y: viewportRef.current.scrollTop, + }); }, children: (
Date: Fri, 20 Dec 2024 20:16:24 +1100 Subject: [PATCH 03/10] [docs] Fix typos (#1190) --- docs/reference/generated/form.json | 2 +- docs/reference/generated/menu-arrow.json | 2 +- docs/reference/generated/menu-popup.json | 2 +- docs/reference/generated/menu-positioner.json | 2 +- docs/reference/generated/popover-arrow.json | 2 +- docs/reference/generated/popover-popup.json | 2 +- docs/reference/generated/popover-positioner.json | 2 +- docs/reference/generated/preview-card-arrow.json | 2 +- docs/reference/generated/preview-card-popup.json | 2 +- docs/reference/generated/preview-card-positioner.json | 2 +- docs/reference/generated/tooltip-arrow.json | 2 +- docs/reference/generated/tooltip-popup.json | 2 +- docs/reference/generated/tooltip-positioner.json | 2 +- .../app/(public)/(content)/react/handbook/animation/page.mdx | 2 +- packages/react/src/form/Form.tsx | 4 ++-- packages/react/src/menu/arrow/MenuArrowDataAttributes.ts | 2 +- packages/react/src/menu/popup/MenuPopupDataAttributes.ts | 2 +- .../react/src/menu/positioner/MenuPositionerDataAttributes.ts | 2 +- .../react/src/popover/arrow/PopoverArrowDataAttributes.ts | 2 +- .../react/src/popover/popup/PopoverPopupDataAttributes.ts | 2 +- .../src/popover/positioner/PopoverPositionerDataAttributes.ts | 2 +- .../src/preview-card/arrow/PreviewCardArrowDataAttributes.ts | 2 +- .../src/preview-card/popup/PreviewCardPopupDataAttributes.ts | 2 +- .../positioner/PreviewCardPositionerDataAttributes.ts | 2 +- .../react/src/tooltip/arrow/TooltipArrowDataAttributes.ts | 2 +- .../react/src/tooltip/popup/TooltipPopupDataAttributes.ts | 2 +- .../src/tooltip/positioner/TooltipPositionerDataAttributes.ts | 2 +- 27 files changed, 28 insertions(+), 28 deletions(-) diff --git a/docs/reference/generated/form.json b/docs/reference/generated/form.json index 189b1c0eb1..2bc0908006 100644 --- a/docs/reference/generated/form.json +++ b/docs/reference/generated/form.json @@ -4,7 +4,7 @@ "props": { "errors": { "type": "Record", - "description": "An object where the keys correspond to the `name` attribute of the form fields,\nand the values correspond to the erorr(s) related to that field." + "description": "An object where the keys correspond to the `name` attribute of the form fields,\nand the values correspond to the error(s) related to that field." }, "onClearErrors": { "type": "(errors) => void", diff --git a/docs/reference/generated/menu-arrow.json b/docs/reference/generated/menu-arrow.json index 98e8167679..56a43087cb 100644 --- a/docs/reference/generated/menu-arrow.json +++ b/docs/reference/generated/menu-arrow.json @@ -26,7 +26,7 @@ }, "data-side": { "description": "Indicates which side the menu is positioned relative to the trigger.", - "type": "'none' | 'top' | 'right' | 'bottom' | 'left'" + "type": "'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start'" } }, "cssVariables": {} diff --git a/docs/reference/generated/menu-popup.json b/docs/reference/generated/menu-popup.json index 831aa6a6f2..198aed3a64 100644 --- a/docs/reference/generated/menu-popup.json +++ b/docs/reference/generated/menu-popup.json @@ -20,7 +20,7 @@ }, "data-side": { "description": "Indicates which side the menu is positioned relative to the trigger.", - "type": "'none' | 'top' | 'right' | 'bottom' | 'left'" + "type": "'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start'" }, "data-starting-style": { "description": "Present when the menu is animating in." diff --git a/docs/reference/generated/menu-positioner.json b/docs/reference/generated/menu-positioner.json index f1f0ebfcbc..dc8fc9f0ac 100644 --- a/docs/reference/generated/menu-positioner.json +++ b/docs/reference/generated/menu-positioner.json @@ -75,7 +75,7 @@ }, "data-side": { "description": "Indicates which side the menu is positioned relative to the trigger.", - "type": "'none' | 'top' | 'right' | 'bottom' | 'left'" + "type": "'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start'" } }, "cssVariables": { diff --git a/docs/reference/generated/popover-arrow.json b/docs/reference/generated/popover-arrow.json index a21c4cf3d9..0da3f942a4 100644 --- a/docs/reference/generated/popover-arrow.json +++ b/docs/reference/generated/popover-arrow.json @@ -26,7 +26,7 @@ }, "data-side": { "description": "Indicates which side the popup is positioned relative to the trigger.", - "type": "'none' | 'top' | 'right' | 'bottom' | 'left'" + "type": "'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start'" } }, "cssVariables": {} diff --git a/docs/reference/generated/popover-popup.json b/docs/reference/generated/popover-popup.json index 0b4a5e07b4..312782a02f 100644 --- a/docs/reference/generated/popover-popup.json +++ b/docs/reference/generated/popover-popup.json @@ -28,7 +28,7 @@ }, "data-side": { "description": "Indicates which side the popup is positioned relative to the trigger.", - "type": "'none' | 'top' | 'right' | 'bottom' | 'left'" + "type": "'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start'" }, "data-starting-style": { "description": "Present when the popup is animating in." diff --git a/docs/reference/generated/popover-positioner.json b/docs/reference/generated/popover-positioner.json index a8d16717c9..9a155f18dd 100644 --- a/docs/reference/generated/popover-positioner.json +++ b/docs/reference/generated/popover-positioner.json @@ -77,7 +77,7 @@ }, "data-side": { "description": "Indicates which side the popup is positioned relative to the trigger.", - "type": "'none' | 'top' | 'right' | 'bottom' | 'left'" + "type": "'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start'" } }, "cssVariables": { diff --git a/docs/reference/generated/preview-card-arrow.json b/docs/reference/generated/preview-card-arrow.json index 7cee7ac549..b12b57add6 100644 --- a/docs/reference/generated/preview-card-arrow.json +++ b/docs/reference/generated/preview-card-arrow.json @@ -26,7 +26,7 @@ }, "data-side": { "description": "Indicates which side the preview card is positioned relative to the trigger.", - "type": "'none' | 'top' | 'right' | 'bottom' | 'left'" + "type": "'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start'" } }, "cssVariables": {} diff --git a/docs/reference/generated/preview-card-popup.json b/docs/reference/generated/preview-card-popup.json index 33e86f81e3..70606fac3c 100644 --- a/docs/reference/generated/preview-card-popup.json +++ b/docs/reference/generated/preview-card-popup.json @@ -20,7 +20,7 @@ }, "data-side": { "description": "Indicates which side the preview card is positioned relative to the trigger.", - "type": "'none' | 'top' | 'right' | 'bottom' | 'left'" + "type": "'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start'" }, "data-starting-style": { "description": "Present when the preview card is animating in." diff --git a/docs/reference/generated/preview-card-positioner.json b/docs/reference/generated/preview-card-positioner.json index 491da9dd91..5fea11a83e 100644 --- a/docs/reference/generated/preview-card-positioner.json +++ b/docs/reference/generated/preview-card-positioner.json @@ -77,7 +77,7 @@ }, "data-side": { "description": "Indicates which side the preview card is positioned relative to the trigger.", - "type": "'none' | 'top' | 'right' | 'bottom' | 'left'" + "type": "'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start'" } }, "cssVariables": { diff --git a/docs/reference/generated/tooltip-arrow.json b/docs/reference/generated/tooltip-arrow.json index 30160656a1..f122a0ebac 100644 --- a/docs/reference/generated/tooltip-arrow.json +++ b/docs/reference/generated/tooltip-arrow.json @@ -26,7 +26,7 @@ }, "data-side": { "description": "Indicates which side the tooltip is positioned relative to the trigger.", - "type": "'none' | 'top' | 'right' | 'bottom' | 'left'" + "type": "'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start'" } }, "cssVariables": {} diff --git a/docs/reference/generated/tooltip-popup.json b/docs/reference/generated/tooltip-popup.json index 4aa77e2744..56325e4847 100644 --- a/docs/reference/generated/tooltip-popup.json +++ b/docs/reference/generated/tooltip-popup.json @@ -24,7 +24,7 @@ }, "data-side": { "description": "Indicates which side the tooltip is positioned relative to the trigger.", - "type": "'none' | 'top' | 'right' | 'bottom' | 'left'" + "type": "'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start'" }, "data-starting-style": { "description": "Present when the tooltip is animating in." diff --git a/docs/reference/generated/tooltip-positioner.json b/docs/reference/generated/tooltip-positioner.json index b7e0021f1a..538059bb6a 100644 --- a/docs/reference/generated/tooltip-positioner.json +++ b/docs/reference/generated/tooltip-positioner.json @@ -77,7 +77,7 @@ }, "data-side": { "description": "Indicates which side the tooltip is positioned relative to the trigger.", - "type": "'none' | 'top' | 'right' | 'bottom' | 'left'" + "type": "'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start'" } }, "cssVariables": { diff --git a/docs/src/app/(public)/(content)/react/handbook/animation/page.mdx b/docs/src/app/(public)/(content)/react/handbook/animation/page.mdx index e455907ac5..792148415a 100644 --- a/docs/src/app/(public)/(content)/react/handbook/animation/page.mdx +++ b/docs/src/app/(public)/(content)/react/handbook/animation/page.mdx @@ -38,7 +38,7 @@ For example, if the user closes a popup before it finishes opening, with CSS tra Use the following Base UI attributes for creating CSS animations when a component becomes visible or hidden: - `[data-open]` corresponds to the style applied when a component becomes visible. -- `[data-ending-style]` corresponds the style applied before a component becomes hidden. +- `[data-closed]` corresponds the style applied before a component becomes hidden. ```css title="popover.css" @keyframes scaleIn { diff --git a/packages/react/src/form/Form.tsx b/packages/react/src/form/Form.tsx index 2b49c2b422..106947bf24 100644 --- a/packages/react/src/form/Form.tsx +++ b/packages/react/src/form/Form.tsx @@ -93,7 +93,7 @@ namespace Form { export interface Props extends BaseUIComponentProps<'form', State> { /** * An object where the keys correspond to the `name` attribute of the form fields, - * and the values correspond to the erorr(s) related to that field. + * and the values correspond to the error(s) related to that field. */ errors?: FormContext['errors']; /** @@ -120,7 +120,7 @@ Form.propTypes /* remove-proptypes */ = { className: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), /** * An object where the keys correspond to the `name` attribute of the form fields, - * and the values correspond to the erorr(s) related to that field. + * and the values correspond to the error(s) related to that field. */ errors: PropTypes.object, /** diff --git a/packages/react/src/menu/arrow/MenuArrowDataAttributes.ts b/packages/react/src/menu/arrow/MenuArrowDataAttributes.ts index 6d407a0cf3..e3d0b6ea92 100644 --- a/packages/react/src/menu/arrow/MenuArrowDataAttributes.ts +++ b/packages/react/src/menu/arrow/MenuArrowDataAttributes.ts @@ -13,7 +13,7 @@ export enum MenuArrowDataAttributes { anchorHidden = 'data-anchor-hidden', /** * Indicates which side the menu is positioned relative to the trigger. - * @type {'none' | 'top' | 'right' | 'bottom' | 'left'} + * @type {'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start'} */ side = 'data-side', /** diff --git a/packages/react/src/menu/popup/MenuPopupDataAttributes.ts b/packages/react/src/menu/popup/MenuPopupDataAttributes.ts index 567bb0f4e1..1828cdcf7b 100644 --- a/packages/react/src/menu/popup/MenuPopupDataAttributes.ts +++ b/packages/react/src/menu/popup/MenuPopupDataAttributes.ts @@ -17,7 +17,7 @@ export enum MenuPopupDataAttributes { endingStyle = 'data-ending-style', /** * Indicates which side the menu is positioned relative to the trigger. - * @type {'none' | 'top' | 'right' | 'bottom' | 'left'} + * @type {'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start'} */ side = 'data-side', } diff --git a/packages/react/src/menu/positioner/MenuPositionerDataAttributes.ts b/packages/react/src/menu/positioner/MenuPositionerDataAttributes.ts index 456e619956..6fa553aa2c 100644 --- a/packages/react/src/menu/positioner/MenuPositionerDataAttributes.ts +++ b/packages/react/src/menu/positioner/MenuPositionerDataAttributes.ts @@ -13,7 +13,7 @@ export enum MenuPositionerDataAttributes { anchorHidden = 'data-anchor-hidden', /** * Indicates which side the menu is positioned relative to the trigger. - * @type {'none' | 'top' | 'right' | 'bottom' | 'left'} + * @type {'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start'} */ side = 'data-side', } diff --git a/packages/react/src/popover/arrow/PopoverArrowDataAttributes.ts b/packages/react/src/popover/arrow/PopoverArrowDataAttributes.ts index 5acd964ea4..5871cbc87b 100644 --- a/packages/react/src/popover/arrow/PopoverArrowDataAttributes.ts +++ b/packages/react/src/popover/arrow/PopoverArrowDataAttributes.ts @@ -13,7 +13,7 @@ export enum PopoverArrowDataAttributes { anchorHidden = 'data-anchor-hidden', /** * Indicates which side the popup is positioned relative to the trigger. - * @type {'none' | 'top' | 'right' | 'bottom' | 'left'} + * @type {'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start'} */ side = 'data-side', /** diff --git a/packages/react/src/popover/popup/PopoverPopupDataAttributes.ts b/packages/react/src/popover/popup/PopoverPopupDataAttributes.ts index ac855000f1..14a4ae6444 100644 --- a/packages/react/src/popover/popup/PopoverPopupDataAttributes.ts +++ b/packages/react/src/popover/popup/PopoverPopupDataAttributes.ts @@ -17,7 +17,7 @@ export enum PopoverPopupDataAttributes { endingStyle = 'data-ending-style', /** * Indicates which side the popup is positioned relative to the trigger. - * @type {'none' | 'top' | 'right' | 'bottom' | 'left'} + * @type {'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start'} */ side = 'data-side', } diff --git a/packages/react/src/popover/positioner/PopoverPositionerDataAttributes.ts b/packages/react/src/popover/positioner/PopoverPositionerDataAttributes.ts index 9067baaa42..87da1ede04 100644 --- a/packages/react/src/popover/positioner/PopoverPositionerDataAttributes.ts +++ b/packages/react/src/popover/positioner/PopoverPositionerDataAttributes.ts @@ -13,7 +13,7 @@ export enum PopoverPositionerDataAttributes { anchorHidden = 'data-anchor-hidden', /** * Indicates which side the popup is positioned relative to the trigger. - * @type {'none' | 'top' | 'right' | 'bottom' | 'left'} + * @type {'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start'} */ side = 'data-side', } diff --git a/packages/react/src/preview-card/arrow/PreviewCardArrowDataAttributes.ts b/packages/react/src/preview-card/arrow/PreviewCardArrowDataAttributes.ts index 0acae052d4..87023917c8 100644 --- a/packages/react/src/preview-card/arrow/PreviewCardArrowDataAttributes.ts +++ b/packages/react/src/preview-card/arrow/PreviewCardArrowDataAttributes.ts @@ -13,7 +13,7 @@ export enum PreviewCardArrowDataAttributes { anchorHidden = 'data-anchor-hidden', /** * Indicates which side the preview card is positioned relative to the trigger. - * @type {'none' | 'top' | 'right' | 'bottom' | 'left'} + * @type {'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start'} */ side = 'data-side', /** diff --git a/packages/react/src/preview-card/popup/PreviewCardPopupDataAttributes.ts b/packages/react/src/preview-card/popup/PreviewCardPopupDataAttributes.ts index da89b87af8..007c8dbf41 100644 --- a/packages/react/src/preview-card/popup/PreviewCardPopupDataAttributes.ts +++ b/packages/react/src/preview-card/popup/PreviewCardPopupDataAttributes.ts @@ -17,7 +17,7 @@ export enum PreviewCardPopupDataAttributes { endingStyle = 'data-ending-style', /** * Indicates which side the preview card is positioned relative to the trigger. - * @type {'none' | 'top' | 'right' | 'bottom' | 'left'} + * @type {'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start'} */ side = 'data-side', } diff --git a/packages/react/src/preview-card/positioner/PreviewCardPositionerDataAttributes.ts b/packages/react/src/preview-card/positioner/PreviewCardPositionerDataAttributes.ts index 6925582cc5..3bb786dee4 100644 --- a/packages/react/src/preview-card/positioner/PreviewCardPositionerDataAttributes.ts +++ b/packages/react/src/preview-card/positioner/PreviewCardPositionerDataAttributes.ts @@ -13,7 +13,7 @@ export enum PreviewCardPositionerDataAttributes { anchorHidden = 'data-anchor-hidden', /** * Indicates which side the preview card is positioned relative to the trigger. - * @type {'none' | 'top' | 'right' | 'bottom' | 'left'} + * @type {'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start'} */ side = 'data-side', } diff --git a/packages/react/src/tooltip/arrow/TooltipArrowDataAttributes.ts b/packages/react/src/tooltip/arrow/TooltipArrowDataAttributes.ts index 7ff12d955d..f0dd323ca4 100644 --- a/packages/react/src/tooltip/arrow/TooltipArrowDataAttributes.ts +++ b/packages/react/src/tooltip/arrow/TooltipArrowDataAttributes.ts @@ -13,7 +13,7 @@ export enum TooltipArrowDataAttributes { anchorHidden = 'data-anchor-hidden', /** * Indicates which side the tooltip is positioned relative to the trigger. - * @type {'none' | 'top' | 'right' | 'bottom' | 'left'} + * @type {'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start'} */ side = 'data-side', /** diff --git a/packages/react/src/tooltip/popup/TooltipPopupDataAttributes.ts b/packages/react/src/tooltip/popup/TooltipPopupDataAttributes.ts index 26052c278d..9bf2420a26 100644 --- a/packages/react/src/tooltip/popup/TooltipPopupDataAttributes.ts +++ b/packages/react/src/tooltip/popup/TooltipPopupDataAttributes.ts @@ -17,7 +17,7 @@ export enum TooltipPopupDataAttributes { endingStyle = 'data-ending-style', /** * Indicates which side the tooltip is positioned relative to the trigger. - * @type {'none' | 'top' | 'right' | 'bottom' | 'left'} + * @type {'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start'} */ side = 'data-side', /** diff --git a/packages/react/src/tooltip/positioner/TooltipPositionerDataAttributes.ts b/packages/react/src/tooltip/positioner/TooltipPositionerDataAttributes.ts index 88f2929051..6dfea51321 100644 --- a/packages/react/src/tooltip/positioner/TooltipPositionerDataAttributes.ts +++ b/packages/react/src/tooltip/positioner/TooltipPositionerDataAttributes.ts @@ -13,7 +13,7 @@ export enum TooltipPositionerDataAttributes { anchorHidden = 'data-anchor-hidden', /** * Indicates which side the tooltip is positioned relative to the trigger. - * @type {'none' | 'top' | 'right' | 'bottom' | 'left'} + * @type {'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start'} */ side = 'data-side', } From 331e3b70a0a20e949887398f80b2eb984208e9b4 Mon Sep 17 00:00:00 2001 From: Vlad Moroz Date: Fri, 20 Dec 2024 10:19:15 +0100 Subject: [PATCH 04/10] [docs] Sturdier quick nav math (#1182) --- docs/src/components/QuickNav/QuickNav.tsx | 36 ++++++++++++++--------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/docs/src/components/QuickNav/QuickNav.tsx b/docs/src/components/QuickNav/QuickNav.tsx index d93a6e231c..e05efe1940 100644 --- a/docs/src/components/QuickNav/QuickNav.tsx +++ b/docs/src/components/QuickNav/QuickNav.tsx @@ -65,16 +65,17 @@ function onMounted(ref: React.RefObject) { ref.current.style.top = '0px'; ref.current.style.bottom = ''; - ref.current.style.marginTop = '0px'; + ref.current.style.marginTop = ''; ref.current.style.marginBottom = ''; // Get the nav top Y coordinate from the start of the document // if it was `position: static` and `position: absolute` // relative to the start of the document ref.current.style.position = 'static'; - const staticTop = window.scrollY + ref.current.getBoundingClientRect().y; + const staticTop = window.scrollY + Math.round(ref.current.getBoundingClientRect().y); + ref.current.style.marginTop = '0px'; ref.current.style.position = 'absolute'; - const absoluteTop = window.scrollY + ref.current.getBoundingClientRect().y; + const absoluteTop = window.scrollY + Math.round(ref.current.getBoundingClientRect().y); // Get the nav bottom Y coordinate when it's at its maximum possible bottom position // relative to the start of the document @@ -82,7 +83,7 @@ function onMounted(ref: React.RefObject) { ref.current.style.top = 'auto'; ref.current.style.bottom = '0'; const rect = ref.current.getBoundingClientRect(); - const absoluteBottom = window.scrollY + rect.bottom; + const absoluteBottom = window.scrollY + Math.round(rect.bottom); ref.current.style.position = ''; ref.current.style.top = initialStyles.top; @@ -159,14 +160,12 @@ function onMounted(ref: React.RefObject) { if (!ref.current || isScrollLocked) { return; } - const delta = window.scrollY - prevScrollY; prevScrollY = window.scrollY; - // We may get into <0.1px rounding issues in Safari and Firefox const rect = ref.current.getBoundingClientRect(); - top = Math.round(rect.top); - bottom = Math.round(rect.bottom); + top = rect.top; + bottom = rect.bottom; // Skip when scrolling in the direction that matches the sticky position if ((delta > 0 && state === 'StickyBottom') || (delta < 0 && state === 'StickyTop')) { @@ -184,11 +183,12 @@ function onMounted(ref: React.RefObject) { if (state === 'StickyTop') { const clippedAtBottom = bottom - window.innerHeight; - if (clippedAtBottom - top >= stickyTopThreshold) { + if (clippedAtBottom - top > stickyTopThreshold) { if (delta >= clippedAtBottom) { stickToBottom(); - } else { - unstick(top - delta, bottom - delta); + // Unstick if we are scrolling down (and not recovering from overscroll) + } else if (delta > 0 && !isOverscrolling()) { + unstick(Math.round(top) - delta, Math.round(bottom) - delta); } } return; @@ -197,13 +197,14 @@ function onMounted(ref: React.RefObject) { if (state === 'StickyBottom') { if (delta <= top) { stickToTop(); - } else { - unstick(top - delta, bottom - delta); + // Unstick if we are scrolling up (and not recovering from overscroll) + } else if (delta < 0 && !isOverscrolling()) { + unstick(Math.round(top) - delta, Math.round(bottom) - delta); } return; } - if (state === 'Scrollable' && delta <= 0 && top - delta >= cssTop) { + if (state === 'Scrollable' && delta < 0 && top - delta >= cssTop) { stickToTop(); return; } @@ -259,6 +260,13 @@ function onMounted(ref: React.RefObject) { }; } +function isOverscrolling() { + return ( + window.scrollY < 0 || + window.scrollY + window.innerHeight > document.documentElement.scrollHeight + ); +} + export function Title({ className, ...props }: React.ComponentProps<'header'>) { return
; } From e04f4252265736f9160c7b94c7017091f2b7e586 Mon Sep 17 00:00:00 2001 From: Vlad Moroz Date: Fri, 20 Dec 2024 12:53:53 +0100 Subject: [PATCH 05/10] [docs] Full Tailwind v3 compatibility (#1185) --- .../accordion/demos/hero/tailwind/index.tsx | 12 ++--- .../demos/hero/tailwind/index.tsx | 10 ++--- .../demos/hero/tailwind/index.tsx | 6 +-- .../checkbox/demos/hero/tailwind/index.tsx | 2 +- .../collapsible/demos/hero/tailwind/index.tsx | 4 +- .../css-modules/index.module.css | 1 + .../close-confirmation/tailwind/index.tsx | 18 ++++---- .../dialog/demos/hero/tailwind/index.tsx | 8 ++-- .../demos/nested/css-modules/index.module.css | 1 + .../dialog/demos/nested/tailwind/index.tsx | 14 +++--- .../field/demos/hero/tailwind/index.tsx | 2 +- .../fieldset/demos/hero/tailwind/index.tsx | 4 +- .../form/demos/hero/tailwind/index.tsx | 4 +- .../input/demos/hero/tailwind/index.tsx | 2 +- .../demos/checkbox-items/tailwind/index.tsx | 12 ++--- .../demos/group-labels/tailwind/index.tsx | 18 ++++---- .../menu/demos/hero/tailwind/index.tsx | 18 ++++---- .../demos/open-on-hover/tailwind/index.tsx | 14 +++--- .../menu/demos/radio-items/tailwind/index.tsx | 12 ++--- .../menu/demos/submenu/tailwind/index.tsx | 34 +++++++------- .../demos/hero/tailwind/index.tsx | 2 +- .../popover/demos/hero/tailwind/index.tsx | 4 +- .../demos/hero/tailwind/index.tsx | 4 +- .../radio/demos/hero/tailwind/index.tsx | 6 +-- .../scroll-area/demos/hero/tailwind/index.tsx | 4 +- .../select/demos/hero/tailwind/index.tsx | 14 +++--- .../separator/demos/hero/tailwind/index.tsx | 12 ++--- .../slider/demos/hero/tailwind/index.tsx | 2 +- .../switch/demos/hero/tailwind/index.tsx | 2 +- .../tabs/demos/hero/tailwind/index.tsx | 14 +++--- .../demos/hero/tailwind/index.tsx | 6 +-- .../toggle/demos/hero/tailwind/index.tsx | 2 +- .../tooltip/demos/hero/tailwind/index.tsx | 12 ++--- .../(content)/react/handbook/styling/page.mdx | 10 ++--- .../demos/hero/tailwind/index.tsx | 2 +- docs/src/components/Demo/CodeSandboxLink.tsx | 45 ++++++++++++++++--- 36 files changed, 185 insertions(+), 152 deletions(-) diff --git a/docs/src/app/(public)/(content)/react/components/accordion/demos/hero/tailwind/index.tsx b/docs/src/app/(public)/(content)/react/components/accordion/demos/hero/tailwind/index.tsx index b4a071d0e8..8ba8a16ddd 100644 --- a/docs/src/app/(public)/(content)/react/components/accordion/demos/hero/tailwind/index.tsx +++ b/docs/src/app/(public)/(content)/react/components/accordion/demos/hero/tailwind/index.tsx @@ -6,12 +6,12 @@ export default function ExampleAccordion() { - + What is Base UI? - +
Base UI is a library of high-quality unstyled React components for design systems and web apps. @@ -21,12 +21,12 @@ export default function ExampleAccordion() { - + How do I get started? - +
Head to the “Quick start” guide in the docs. If you’ve used unstyled libraries before, you’ll feel at home. @@ -36,12 +36,12 @@ export default function ExampleAccordion() { - + Can I use it for my project? - +
Of course! Base UI is free and open source.
diff --git a/docs/src/app/(public)/(content)/react/components/alert-dialog/demos/hero/tailwind/index.tsx b/docs/src/app/(public)/(content)/react/components/alert-dialog/demos/hero/tailwind/index.tsx index 7ee26c6c74..09e804af53 100644 --- a/docs/src/app/(public)/(content)/react/components/alert-dialog/demos/hero/tailwind/index.tsx +++ b/docs/src/app/(public)/(content)/react/components/alert-dialog/demos/hero/tailwind/index.tsx @@ -4,12 +4,12 @@ import { AlertDialog } from '@base-ui-components/react/alert-dialog'; export default function ExampleAlertDialog() { return ( - + Discard draft - - + + Discard draft? @@ -17,10 +17,10 @@ export default function ExampleAlertDialog() { You can’t undo this action.
- + Cancel - + Discard
diff --git a/docs/src/app/(public)/(content)/react/components/checkbox-group/demos/hero/tailwind/index.tsx b/docs/src/app/(public)/(content)/react/components/checkbox-group/demos/hero/tailwind/index.tsx index fdeed1ce15..7cf326202d 100644 --- a/docs/src/app/(public)/(content)/react/components/checkbox-group/demos/hero/tailwind/index.tsx +++ b/docs/src/app/(public)/(content)/react/components/checkbox-group/demos/hero/tailwind/index.tsx @@ -16,7 +16,7 @@ export default function ExampleCheckboxGroup() {