From fa9515bdea3afa536e9e4d84e41f810b53fa581e Mon Sep 17 00:00:00 2001 From: atomiks Date: Thu, 12 Dec 2024 09:35:18 +1100 Subject: [PATCH] [ScrollArea] Remove `gutter` prop (#1023) --- .../components/scroll-area/ScrollAreaInset.js | 113 ------------------ .../scroll-area/ScrollAreaInset.tsx | 113 ------------------ .../components/scroll-area/scroll-area.mdx | 10 -- .../reference/generated/scroll-area-root.json | 5 - .../experiments/scroll-area-inset.module.css | 41 +++++++ .../experiments/scroll-area-inset.tsx | 33 +++++ .../scroll-area/root/ScrollAreaRoot.test.tsx | 58 --------- .../src/scroll-area/root/ScrollAreaRoot.tsx | 20 +--- .../scroll-area/root/ScrollAreaRootContext.ts | 1 - .../src/scroll-area/root/useScrollAreaRoot.ts | 1 - .../viewport/useScrollAreaViewport.tsx | 21 ---- 11 files changed, 78 insertions(+), 338 deletions(-) delete mode 100644 docs/data/components/scroll-area/ScrollAreaInset.js delete mode 100644 docs/data/components/scroll-area/ScrollAreaInset.tsx create mode 100644 docs/src/app/(private)/experiments/scroll-area-inset.module.css create mode 100644 docs/src/app/(private)/experiments/scroll-area-inset.tsx diff --git a/docs/data/components/scroll-area/ScrollAreaInset.js b/docs/data/components/scroll-area/ScrollAreaInset.js deleted file mode 100644 index 3126e82a1f..0000000000 --- a/docs/data/components/scroll-area/ScrollAreaInset.js +++ /dev/null @@ -1,113 +0,0 @@ -'use client'; -import * as React from 'react'; -import { ScrollArea } from '@base-ui-components/react/scroll-area'; -import { styled } from '@mui/system'; - -const data = [ - 'f47ac10b-58cc-4372-a567-0e02b2c3d479', - '7c9e6679-7425-40de-944b-e07fc1f90ae7', - '550e8400-e29b-41d4-a716-446655440000', - '9b2b38e2-4c7b-4e53-a228-c89c535c5072', - '3fa85f64-5717-4562-b3fc-2c963f66afa6', - '4dfbdfc4-2d0e-4e6c-8bd6-7c8d765f0a1c', - 'aa9e5d30-cf2a-4234-bc9b-6a5d965c6a00', - '16fd2706-8baf-433b-82eb-8c7fada847da', - '66ed7a57-e4b7-4b82-8b1e-2a8942f8ec6e', - 'f9e87c8f-7b4f-4c7e-bb72-ebe8e2277c5e', -]; - -const scrollbarSize = 10; - -export default function ScrollAreaInset() { - return ( - - -
-

User IDs

-
    - {data.map((value) => ( -
  • - {value} -
  • - ))} -
-
-
- - - - - - - -
- ); -} - -const ScrollAreaRoot = styled(ScrollArea.Root)` - width: 250px; - height: 250px; - border-radius: 2px; - background: #f5f5f5; - - --scrollbar-size: ${scrollbarSize}px; -`; - -const ScrollAreaViewport = styled(ScrollArea.Viewport)` - width: 100%; - height: 100%; - border-radius: 2px; - - &:focus-visible { - outline: 2px solid rgb(0 0 0 / 0.5); - } -`; - -const ScrollAreaScrollbar = styled(ScrollArea.Scrollbar)` - background: rgb(220 220 220); - box-sizing: border-box; - display: flex; - - &[data-orientation='vertical'] { - width: var(--scrollbar-size); - } - - &[data-orientation='horizontal'] { - flex-direction: column; - height: var(--scrollbar-size); - } -`; - -const ScrollAreaThumb = styled(ScrollArea.Thumb)` - background: rgb(180 180 180); - flex: 1; - - &:hover { - background: rgb(150 150 150); - } - - &::before { - content: ''; - display: block; - position: absolute; - width: 100%; - height: 100%; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - min-width: 22px; - min-height: 22px; - } -`; - -const ScrollAreaCorner = styled(ScrollArea.Corner)` - background: rgb(220 220 220); -`; diff --git a/docs/data/components/scroll-area/ScrollAreaInset.tsx b/docs/data/components/scroll-area/ScrollAreaInset.tsx deleted file mode 100644 index 3126e82a1f..0000000000 --- a/docs/data/components/scroll-area/ScrollAreaInset.tsx +++ /dev/null @@ -1,113 +0,0 @@ -'use client'; -import * as React from 'react'; -import { ScrollArea } from '@base-ui-components/react/scroll-area'; -import { styled } from '@mui/system'; - -const data = [ - 'f47ac10b-58cc-4372-a567-0e02b2c3d479', - '7c9e6679-7425-40de-944b-e07fc1f90ae7', - '550e8400-e29b-41d4-a716-446655440000', - '9b2b38e2-4c7b-4e53-a228-c89c535c5072', - '3fa85f64-5717-4562-b3fc-2c963f66afa6', - '4dfbdfc4-2d0e-4e6c-8bd6-7c8d765f0a1c', - 'aa9e5d30-cf2a-4234-bc9b-6a5d965c6a00', - '16fd2706-8baf-433b-82eb-8c7fada847da', - '66ed7a57-e4b7-4b82-8b1e-2a8942f8ec6e', - 'f9e87c8f-7b4f-4c7e-bb72-ebe8e2277c5e', -]; - -const scrollbarSize = 10; - -export default function ScrollAreaInset() { - return ( - - -
-

User IDs

-
    - {data.map((value) => ( -
  • - {value} -
  • - ))} -
-
-
- - - - - - - -
- ); -} - -const ScrollAreaRoot = styled(ScrollArea.Root)` - width: 250px; - height: 250px; - border-radius: 2px; - background: #f5f5f5; - - --scrollbar-size: ${scrollbarSize}px; -`; - -const ScrollAreaViewport = styled(ScrollArea.Viewport)` - width: 100%; - height: 100%; - border-radius: 2px; - - &:focus-visible { - outline: 2px solid rgb(0 0 0 / 0.5); - } -`; - -const ScrollAreaScrollbar = styled(ScrollArea.Scrollbar)` - background: rgb(220 220 220); - box-sizing: border-box; - display: flex; - - &[data-orientation='vertical'] { - width: var(--scrollbar-size); - } - - &[data-orientation='horizontal'] { - flex-direction: column; - height: var(--scrollbar-size); - } -`; - -const ScrollAreaThumb = styled(ScrollArea.Thumb)` - background: rgb(180 180 180); - flex: 1; - - &:hover { - background: rgb(150 150 150); - } - - &::before { - content: ''; - display: block; - position: absolute; - width: 100%; - height: 100%; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - min-width: 22px; - min-height: 22px; - } -`; - -const ScrollAreaCorner = styled(ScrollArea.Corner)` - background: rgb(220 220 220); -`; diff --git a/docs/data/components/scroll-area/scroll-area.mdx b/docs/data/components/scroll-area/scroll-area.mdx index a3e533396b..399a929c9d 100644 --- a/docs/data/components/scroll-area/scroll-area.mdx +++ b/docs/data/components/scroll-area/scroll-area.mdx @@ -46,16 +46,6 @@ The scrollbar elements can be shown conditionally based on the user's interactio } ``` -## Inset scrollbars - -By specifying a `gutter` prop, you can create inset scrollbars that make space for the scrollbar, preventing them from overlapping content. The value should match the size of the scrollbar width/height. - -```jsx - -``` - - - ## Corner The vertical and horizontal scrollbar elements can prevent overlapping each other by rendering a `Corner`: diff --git a/docs/reference/generated/scroll-area-root.json b/docs/reference/generated/scroll-area-root.json index f12e5dfb68..b2fa869d2d 100644 --- a/docs/reference/generated/scroll-area-root.json +++ b/docs/reference/generated/scroll-area-root.json @@ -6,11 +6,6 @@ "type": "string | (state) => string", "description": "CSS class applied to the element, or a function that\nreturns a class based on the component’s state." }, - "gutter": { - "type": "number | string", - "default": "0", - "description": "Determines the space to account for inset scrollbars." - }, "render": { "type": "React.ReactElement | (props, state) => React.ReactElement", "description": "Allows you to replace the component’s HTML element\nwith a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render." diff --git a/docs/src/app/(private)/experiments/scroll-area-inset.module.css b/docs/src/app/(private)/experiments/scroll-area-inset.module.css new file mode 100644 index 0000000000..b82c6efc6a --- /dev/null +++ b/docs/src/app/(private)/experiments/scroll-area-inset.module.css @@ -0,0 +1,41 @@ +.Root { + width: 300px; + height: 300px; + --scrollbar-size: 20px; +} + +.Viewport { + width: 100%; + height: 100%; +} + +.Content { + width: 600px; + height: 600px; + padding-inline-end: var(--scrollbar-size); + padding-bottom: var(--scrollbar-size); +} + +.Scrollbar { + background: lightgray; + + &[data-orientation='vertical'] { + width: var(--scrollbar-size); + } + + &[data-orientation='horizontal'] { + height: var(--scrollbar-size); + } +} + +.Thumb { + background: black; + + &[data-orientation='vertical'] { + width: var(--scrollbar-size); + } + + &[data-orientation='horizontal'] { + height: var(--scrollbar-size); + } +} diff --git a/docs/src/app/(private)/experiments/scroll-area-inset.tsx b/docs/src/app/(private)/experiments/scroll-area-inset.tsx new file mode 100644 index 0000000000..e87b9a7004 --- /dev/null +++ b/docs/src/app/(private)/experiments/scroll-area-inset.tsx @@ -0,0 +1,33 @@ +import * as React from 'react'; +import { ScrollArea } from '@base-ui-components/react/scroll-area'; +import styles from './scroll-area-inset.module.css'; + +export default function ScrollAreaInset() { + return ( +
+

+ Scroll content is not clipped by inset scrollbars (user-defined paddings) +

+ + +
+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim + veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea + commodo consequat. Duis aute irure dolor in reprehenderit in voluptate + velit esse cillum dolore eu fugiat nulla pariatur. +

+
+
+ + + + + + + +
+
+ ); +} diff --git a/packages/react/src/scroll-area/root/ScrollAreaRoot.test.tsx b/packages/react/src/scroll-area/root/ScrollAreaRoot.test.tsx index db53522b92..a531d289e0 100644 --- a/packages/react/src/scroll-area/root/ScrollAreaRoot.test.tsx +++ b/packages/react/src/scroll-area/root/ScrollAreaRoot.test.tsx @@ -72,64 +72,6 @@ describe('', () => { expect(style.paddingRight).to.equal('0px'); expect(style.paddingBottom).to.equal('0px'); }); - - it('should add padding for inset scrollbars', async () => { - await render( - - -
- - - - , - ); - - const contentWrapper = screen.getByTestId('viewport').firstElementChild!; - const style = getComputedStyle(contentWrapper); - - expect(style.paddingRight).to.equal(`${SCROLLBAR_WIDTH}px`); - expect(style.paddingBottom).to.equal(`${SCROLLBAR_HEIGHT}px`); - }); - }); - - describe('prop: dir', () => { - it('should adjust inset padding for rtl', async () => { - await render( - - -
- - - - , - ); - - const contentWrapper = screen.getByTestId('viewport').firstElementChild!; - const style = getComputedStyle(contentWrapper); - - expect(style.paddingLeft).to.equal(`${SCROLLBAR_WIDTH}px`); - expect(style.paddingRight).not.to.equal(`${SCROLLBAR_WIDTH}px`); - expect(style.paddingBottom).to.equal(`${SCROLLBAR_HEIGHT}px`); - }); }); it('accounts for scrollbar padding', async () => { diff --git a/packages/react/src/scroll-area/root/ScrollAreaRoot.tsx b/packages/react/src/scroll-area/root/ScrollAreaRoot.tsx index 28e9dbeb27..a7825ebe0c 100644 --- a/packages/react/src/scroll-area/root/ScrollAreaRoot.tsx +++ b/packages/react/src/scroll-area/root/ScrollAreaRoot.tsx @@ -18,9 +18,9 @@ const ScrollAreaRoot = React.forwardRef(function ScrollAreaRoot( props: ScrollAreaRoot.Props, forwardedRef: React.ForwardedRef, ) { - const { render, className, dir, gutter = 0, ...otherProps } = props; + const { render, className, dir, ...otherProps } = props; - const scrollAreaRoot = useScrollAreaRoot({ dir, gutter }); + const scrollAreaRoot = useScrollAreaRoot({ dir }); const { rootId } = scrollAreaRoot; @@ -36,10 +36,9 @@ const ScrollAreaRoot = React.forwardRef(function ScrollAreaRoot( const contextValue = React.useMemo( () => ({ dir, - gutter, ...scrollAreaRoot, }), - [dir, gutter, scrollAreaRoot], + [dir, scrollAreaRoot], ); const viewportId = `[data-id="${rootId}-viewport"]`; @@ -65,13 +64,7 @@ const ScrollAreaRoot = React.forwardRef(function ScrollAreaRoot( }); namespace ScrollAreaRoot { - export interface Props extends BaseUIComponentProps<'div', State> { - /** - * Determines the space to account for inset scrollbars. - * @default 0 - */ - gutter?: number | string; - } + export interface Props extends BaseUIComponentProps<'div', State> {} export interface State {} } @@ -94,11 +87,6 @@ ScrollAreaRoot.propTypes /* remove-proptypes */ = { * @ignore */ dir: PropTypes.string, - /** - * Determines the space to account for inset scrollbars. - * @default 0 - */ - gutter: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), /** * Allows you to replace the component’s HTML element * with a different tag, or compose it with another component. diff --git a/packages/react/src/scroll-area/root/ScrollAreaRootContext.ts b/packages/react/src/scroll-area/root/ScrollAreaRootContext.ts index 23964499f1..f9d26a558f 100644 --- a/packages/react/src/scroll-area/root/ScrollAreaRootContext.ts +++ b/packages/react/src/scroll-area/root/ScrollAreaRootContext.ts @@ -2,7 +2,6 @@ import * as React from 'react'; export interface ScrollAreaRootContext { dir: string | undefined; - gutter: number | string; cornerSize: { width: number; height: number }; setCornerSize: React.Dispatch>; thumbSize: { width: number; height: number }; diff --git a/packages/react/src/scroll-area/root/useScrollAreaRoot.ts b/packages/react/src/scroll-area/root/useScrollAreaRoot.ts index dd65822a7f..e1edda89b3 100644 --- a/packages/react/src/scroll-area/root/useScrollAreaRoot.ts +++ b/packages/react/src/scroll-area/root/useScrollAreaRoot.ts @@ -241,6 +241,5 @@ export function useScrollAreaRoot(params: useScrollAreaRoot.Parameters) { export namespace useScrollAreaRoot { export interface Parameters { dir: string | undefined; - gutter: number | string; } } diff --git a/packages/react/src/scroll-area/viewport/useScrollAreaViewport.tsx b/packages/react/src/scroll-area/viewport/useScrollAreaViewport.tsx index 81150a3b9b..ed3f242e12 100644 --- a/packages/react/src/scroll-area/viewport/useScrollAreaViewport.tsx +++ b/packages/react/src/scroll-area/viewport/useScrollAreaViewport.tsx @@ -18,7 +18,6 @@ export function useScrollAreaViewport(params: useScrollAreaViewport.Parameters) thumbXRef, cornerRef, dir, - gutter, setCornerSize, setThumbSize, rootId, @@ -175,24 +174,6 @@ export function useScrollAreaViewport(params: useScrollAreaViewport.Parameters) }; }, [computeThumb, viewportRef]); - const wrapperStyles: React.CSSProperties = React.useMemo(() => { - const styles: React.CSSProperties = {}; - - if (!gutter) { - return styles; - } - - // Unconditional for layout stability: prevent layout shifts when the vertical scrollbar is - // hidden/shown. - styles[dir === 'rtl' ? 'paddingLeft' : 'paddingRight'] = gutter; - - if (!hiddenState.scrollbarXHidden) { - styles.paddingBottom = gutter; - } - - return styles; - }, [hiddenState, dir, gutter]); - const getViewportProps = React.useCallback( (externalProps = {}) => mergeReactProps<'div'>(externalProps, { @@ -211,7 +192,6 @@ export function useScrollAreaViewport(params: useScrollAreaViewport.Parameters) ref={contentWrapperRef} style={{ minWidth: 'fit-content', - ...wrapperStyles, }} > {children} @@ -222,7 +202,6 @@ export function useScrollAreaViewport(params: useScrollAreaViewport.Parameters) rootId, hiddenState.scrollbarXHidden, hiddenState.scrollbarYHidden, - wrapperStyles, children, computeThumb, handleScroll,