Skip to content

Commit 17b355d

Browse files
author
v.voloshin
committed
feature: support classNames props
1 parent 3c8706e commit 17b355d

File tree

10 files changed

+285
-87
lines changed

10 files changed

+285
-87
lines changed

src/assets/h-drag.svg

Lines changed: 1 addition & 0 deletions
Loading

src/assets/v-drag.svg

Lines changed: 3 additions & 0 deletions
Loading

src/scrollable.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import * as LazyLoadingStories from './lazy-loading-scrollable.stories';
1313

1414
<Meta title="Docs" />
1515

16-
<Title of={SimpleStories} />
16+
<Title>Docs</Title>
1717

1818
<Description of={SimpleStories} />
1919

src/scrollable.tsx

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,21 @@ import Scrollbar from './scrollbar';
1515
import cx from './utils/classnames';
1616
import generateUniqId from './utils/generateUniqId';
1717
import composeRef from './utils/composeRef';
18+
import makeClassName from './utils/makeClassName';
1819
import useHorizontalScrollbarHandlers from './hooks/useHorizontalScrollbarHandlers';
1920
import useVerticalScrollbarHandlers from './hooks/useVerticalScrollbarHandlers';
2021
import useResizeObserver from './hooks/useResizeObserver';
2122
import useScrollHandlers from './hooks/useScrollHandlers';
2223
import usePointerHandlers from './hooks/usePointerHandlers';
24+
import type { ClassNamesType } from './types';
2325
import './scrollable.css';
2426

27+
export type {
28+
ClassNamesType,
29+
ClassNameStringOrFnType,
30+
ClassNameStringOrFnReturnType,
31+
} from './types';
32+
2533
export type ScrollablePropsType = HTMLAttributes<HTMLElement> & {
2634
/**
2735
* show thumbs on mouse hover, effects only for pointing devices like a mouse
@@ -59,6 +67,19 @@ export type ScrollablePropsType = HTMLAttributes<HTMLElement> & {
5967
* * `[className]__scrollbar__thumb` - thumb element class
6068
*/
6169
className?: string;
70+
/**
71+
* <a name="classnames-props-anchor"></a>
72+
* A set of classes for styling the scrollbar area. The values for the classes can be a string or a function that takes the appropriate argument and returns a string.
73+
*
74+
* Supported classes:
75+
*
76+
* * `scrollable` - the wrapper element class containing the scrollable area and scrollbars, implemented as a dynamic grid.
77+
* * `area` - scrollable element class - uses CSS overflow property
78+
* * `content` - content element class
79+
* * `scrollbar` - scrollbar element class
80+
* * `thumb` - thumb element class
81+
*/
82+
classNames?: Partial<ClassNamesType>;
6283
/**
6384
* Applies styles to the overflow-enabled scrollable element.
6485
* The width and height properties are applied exclusively to the inner content element, excluding scrollbars.
@@ -105,6 +126,7 @@ function Scrollable({
105126
children,
106127
showThumbOnHover = false,
107128
className = undefined,
129+
classNames = undefined,
108130
style = undefined,
109131
wrapperStyle = undefined,
110132
onLeftEdgeReached = undefined,
@@ -166,20 +188,30 @@ function Scrollable({
166188
ignoresScrollEvents,
167189
});
168190

191+
169192
return (
170193
<CssVariables>
171194
<div
172-
className={cx('scrollable', {
173-
'scrollable_has-horizontal-scrollbar': hasHorizontalScrollbar,
174-
'scrollable_has-vertical-scrollbar': hasVerticalScrollbar,
175-
'scrollable_show-mouse-on-hover': showThumbOnHover,
176-
}, className)}
195+
className={cx(
196+
'scrollable',
197+
{
198+
'scrollable_has-horizontal-scrollbar': hasHorizontalScrollbar,
199+
'scrollable_has-vertical-scrollbar': hasVerticalScrollbar,
200+
'scrollable_show-mouse-on-hover': showThumbOnHover,
201+
},
202+
makeClassName(classNames?.scrollable, {
203+
hasHorizontalScrollbar,
204+
hasVerticalScrollbar,
205+
showThumbOnHover,
206+
}),
207+
className
208+
)}
177209
style={wrapperStyle}
178210
>
179211
<div
180212
{...props}
181213
id={scrollableId}
182-
className={cx('scrollable__area', {
214+
className={cx('scrollable__area', makeClassName(classNames?.area), {
183215
[`${className}__area`]: !!className,
184216
})}
185217
style={style}
@@ -188,7 +220,7 @@ function Scrollable({
188220
{...scrollHandlers}
189221
>
190222
<div
191-
className={cx('scrollable__content', {
223+
className={cx('scrollable__content', makeClassName(classNames?.content), {
192224
[`${className}__content`]: !!className,
193225
})}
194226
{...pointerHandlers}
@@ -201,12 +233,14 @@ function Scrollable({
201233
isVertical
202234
aria-controls={scrollableId}
203235
className={className}
236+
classNames={classNames}
204237
{...verticalScrollbarHandlers}
205238
/>
206239
<Scrollbar
207240
ref={hScrollbarRef}
208241
aria-controls={scrollableId}
209242
className={className}
243+
classNames={classNames}
210244
{...horizontalScrollbarHandlers}
211245
/>
212246
<div data-testid="extreme-point" />

src/scrollbar.css

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,6 @@
55
background: var(--scrollbar-background, none);
66
border: var(--scrollbar-border, none);
77

8-
&_vertical &__thumb {
9-
height: 0;
10-
width: var(--thumb-size, 6px);
11-
}
12-
13-
&_horizontal &__thumb {
14-
height: var(--thumb-size, 6px);
15-
width: 0;
16-
}
17-
188
&__track {
199
box-sizing: border-box;
2010
width: 100%;
@@ -29,4 +19,14 @@
2919
cursor: pointer;
3020
touch-action: none;
3121
}
22+
23+
&__thumb_vertical {
24+
height: 0;
25+
width: var(--thumb-size, 6px);
26+
}
27+
28+
&__thumb_horizontal {
29+
height: var(--thumb-size, 6px);
30+
width: 0;
31+
}
3232
}

src/scrollbar.tsx

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,19 @@ import {
66
memo,
77
} from 'react';
88
import cx from './utils/classnames';
9+
import makeClassName from './utils/makeClassName';
10+
import type { ClassNamesType } from './types';
911
import './scrollbar.css';
1012

1113
type ScrollbarPropsType = HTMLAttributes<HTMLElement> & {
1214
isVertical?: boolean;
15+
classNames?: Partial<Pick<ClassNamesType, 'scrollbar' | 'thumb'>>
1316
};
1417

1518
function Scrollbar({
1619
isVertical = false,
1720
className,
21+
classNames,
1822
...props
1923
}: ScrollbarPropsType, ref: Ref<HTMLDivElement>): ReactElement {
2024
const ariaLabel = isVertical
@@ -26,20 +30,31 @@ function Scrollbar({
2630
: 'horizontal';
2731

2832
return (
29-
<div className={cx('scrollable__scrollbar', {
30-
'scrollable__scrollbar_horizontal': !isVertical,
31-
'scrollable__scrollbar_vertical': isVertical,
32-
[`${className}__scrollbar`]: !!className,
33-
})}>
33+
<div className={cx(
34+
'scrollable__scrollbar',
35+
{
36+
'scrollable__scrollbar_horizontal': !isVertical,
37+
'scrollable__scrollbar_vertical': isVertical,
38+
[`${className}__scrollbar`]: !!className,
39+
},
40+
makeClassName(classNames?.scrollbar, { isVertical }),
41+
)}
42+
>
3443
<div className={cx('scrollable__scrollbar__track', {
3544
[`${className}__scrollbar__track`]: !!className,
3645
})}>
3746
<div
3847
{...props}
3948
ref={ref}
40-
className={cx('scrollable__scrollbar__thumb', {
41-
[`${className}__scrollbar__thumb`]: !!className,
42-
})}
49+
className={cx(
50+
'scrollable__scrollbar__thumb',
51+
{
52+
'scrollable__scrollbar__thumb_horizontal': !isVertical,
53+
'scrollable__scrollbar__thumb_vertical': isVertical,
54+
[`${className}__scrollbar__thumb`]: !!className,
55+
},
56+
makeClassName(classNames?.thumb, { isVertical }),
57+
)}
4358
role="scrollbar"
4459
aria-orientation={ariaOrientation}
4560
aria-label={ariaLabel}

src/simple.stories.tsx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,49 @@ const meta = {
1818
options: [false, true],
1919
control: { type: 'radio' },
2020
},
21+
className: { table: { category: 'customization' } },
22+
style: { table: { category: 'customization' } },
23+
wrapperStyle: { table: { category: 'customization' } },
24+
classNames: {
25+
table: {
26+
category: 'customization',
27+
type: {
28+
summary: 'Partial<ClassNamesType>',
29+
detail: `
30+
type ClassNameStringOrFnType<Payload = undefined> =
31+
string
32+
| (Payload extends undefined ? () => string : (payload: Payload) => string);
33+
34+
type ClassNamesType = {
35+
/**
36+
* the wrapper element class containing the scrollable area and scrollbars, implemented as a dynamic grid.
37+
*/
38+
scrollable: ClassNameStringOrFnType;
39+
/**
40+
* scrollable element class - uses CSS overflow property
41+
*/
42+
area: ClassNameStringOrFnType;
43+
/**
44+
* content element class
45+
*/
46+
content: ClassNameStringOrFnType;
47+
/**
48+
* scrollbar element class
49+
*/
50+
scrollbar: ClassNameStringOrFnType<{
51+
isVertical: boolean;
52+
}>;
53+
/**
54+
* thumb element class
55+
*/
56+
thumb: ClassNameStringOrFnType<{
57+
isVertical: boolean;
58+
}>;
59+
}
60+
`,
61+
},
62+
},
63+
},
2164
},
2265
parameters: {
2366
controls: {

0 commit comments

Comments
 (0)