@synerise/ds-scrollbar 1.5.0 → 1.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/CLAUDE.md +135 -0
  3. package/package.json +5 -4
package/CHANGELOG.md CHANGED
@@ -3,6 +3,14 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [1.5.2](https://github.com/Synerise/synerise-design/compare/@synerise/ds-scrollbar@1.5.1...@synerise/ds-scrollbar@1.5.2) (2026-07-23)
7
+
8
+ **Note:** Version bump only for package @synerise/ds-scrollbar
9
+
10
+ ## [1.5.1](https://github.com/Synerise/synerise-design/compare/@synerise/ds-scrollbar@1.5.0...@synerise/ds-scrollbar@1.5.1) (2026-06-17)
11
+
12
+ **Note:** Version bump only for package @synerise/ds-scrollbar
13
+
6
14
  # [1.5.0](https://github.com/Synerise/synerise-design/compare/@synerise/ds-scrollbar@1.4.1...@synerise/ds-scrollbar@1.5.0) (2026-06-11)
7
15
 
8
16
  ### Features
package/CLAUDE.md ADDED
@@ -0,0 +1,135 @@
1
+ # Scrollbar (`@synerise/ds-scrollbar`)
2
+
3
+ > Custom scrollbar component with two rendering strategies: a PerfectScrollbar-based virtual mode and a fully custom drag-and-drop mode.
4
+
5
+ ## Package structure
6
+
7
+ ```
8
+ src/
9
+ Scrollbar.tsx — main component (forwardRef, delegates to DnDScrollbar or VirtualScrollbar)
10
+ Scrollbar.types.ts — ScrollbarAdditionalProps, ScrollbarProps, VirtualScrollbarProps
11
+ Scrollbar.styles.tsx — ScrollbarContainer, LoaderWrapper, Loader
12
+ DnDScrollbar/
13
+ DnDScrollbar.tsx — custom scrollbar with mouse-drag thumb support
14
+ DnDScrollbar.styles.tsx — styled components for DnD variant
15
+ index.ts
16
+ VirtualScrollbar/
17
+ VirtualScrollbar.tsx — PerfectScrollbar wrapper with resize/animation awareness
18
+ VirtualScrollbar.styles.tsx
19
+ index.ts
20
+ __specs__/
21
+ Scrollbar.spec.tsx — Vitest/RTL tests
22
+ style/
23
+ index.less — imports ds-core variables, perfect-scrollbar CSS, mixin
24
+ scrollbar.mixin.less — overrides for .ps__rail-* and .ps__thumb-*, large-size variant
25
+ modules.d.ts — imports @testing-library/jest-dom
26
+ index.ts — public exports
27
+ ```
28
+
29
+ ## Public exports
30
+
31
+ ### `Scrollbar` (default export)
32
+
33
+ The unified entry point. Renders `DnDScrollbar` when `withDnd={true}`, otherwise renders `VirtualScrollbar`. Accepts a `forwardRef` to the underlying scroll container (`HTMLElement`).
34
+
35
+ | Prop | Type | Default | Description |
36
+ |------|------|---------|-------------|
37
+ | `absolute` | `boolean` | `false` | Removes padding offsets from the inner wrapper (scrollbar overlaps content) |
38
+ | `children` | `ReactNode` | — | Scrollable content |
39
+ | `classes` | `string` | — | Extra CSS class(es) applied to the inner content element |
40
+ | `className` | `string` | — | Class applied to the outermost `ScrollbarContainer` div |
41
+ | `confineScroll` | `boolean` | `false` | Prevents wheel events from propagating to parent scrollable areas |
42
+ | `fetchData` | `() => void` | — | Called when scroll reaches the Y end and `hasMore` is `true` and `loading` is falsy |
43
+ | `hasMore` | `boolean` | — | Gates whether `fetchData` is invoked on Y-end |
44
+ | `largeSize` | `boolean` | `false` | Renders a wider (16 px) thumb with blue tokens instead of the default 3 px grey thumb |
45
+ | `loading` | `boolean` | — | Overlays a semi-transparent mask and animated spinner; also dims inner content to `0.2` opacity |
46
+ | `maxHeight` | `string \| number` | — | CSS `max-height` on the scroll content element |
47
+ | `onScroll` | `(event: UIEvent) => void` | — | Fired on scroll |
48
+ | `onYReachEnd` | `() => void` | — | Called independently of `fetchData` whenever the Y end is reached |
49
+ | `overscrollBehavior` | `'auto' \| 'contain' \| 'none'` | `'contain'` | Sets CSS `overscroll-behavior` on the scroll container. Honored by both variants: virtual applies it to the inner `.ps` element, DnD applies it to its own `ScrollbarContainer` |
50
+ | `style` | `CSSProperties` | — | Inline styles for the inner wrapper div |
51
+ | `withDnd` | `boolean` | `false` | Use `DnDScrollbar` (custom thumb, drag support) instead of `VirtualScrollbar` |
52
+
53
+ ### `VirtualScrollbarProps`
54
+
55
+ Extends `ScrollbarProps` with one additional prop:
56
+
57
+ | Prop | Type | Default | Description |
58
+ |------|------|---------|-------------|
59
+ | `scrollbarOptions` | `ScrollBarProps['options']` | — | Options forwarded to `@ofsajd/react-perfect-scrollbar`; `minScrollbarLength` is always forced to `48` |
60
+
61
+ ### Named type exports
62
+
63
+ - `ScrollbarAdditionalProps` — all props except `children`
64
+ - `ScrollbarProps` — `ScrollbarAdditionalProps & { children?: ReactNode }`
65
+ - `VirtualScrollbarProps` — `ScrollbarProps & { scrollbarOptions?: ... }`
66
+ - `OverscrollBehavior` — `'auto' | 'contain' | 'none'`, value of the `overscrollBehavior` prop
67
+
68
+ ## Usage patterns
69
+
70
+ ```tsx
71
+ import Scrollbar from '@synerise/ds-scrollbar';
72
+
73
+ // Default virtual scrollbar
74
+ <Scrollbar maxHeight={400}>
75
+ {content}
76
+ </Scrollbar>
77
+
78
+ // With infinite scroll
79
+ <Scrollbar
80
+ maxHeight={400}
81
+ hasMore={hasMore}
82
+ loading={isLoading}
83
+ fetchData={loadNextPage}
84
+ >
85
+ {items}
86
+ </Scrollbar>
87
+
88
+ // Drag-and-drop custom scrollbar
89
+ <Scrollbar withDnd maxHeight={400}>
90
+ {content}
91
+ </Scrollbar>
92
+
93
+ // Large variant (accessible-style thumb)
94
+ <Scrollbar largeSize maxHeight={400}>
95
+ {content}
96
+ </Scrollbar>
97
+
98
+ // Allow scroll chaining to parent (default is 'contain')
99
+ <Scrollbar maxHeight={400} overscrollBehavior="auto">
100
+ {content}
101
+ </Scrollbar>
102
+ ```
103
+
104
+ ## Styling
105
+
106
+ Two layers of styling:
107
+
108
+ 1. **styled-components** — `ScrollbarContainer` (relative, `height: 100%`) wraps both mode variants. It owns the `.ps { overscroll-behavior: <overscrollBehavior>; }` rule, which is picked up by the virtual variant (perfect-scrollbar's `.ps` element). The DnD variant ships its own `ScrollbarContainer` that applies `overscroll-behavior` directly on itself. The prop defaults to `'contain'`, matching the behavior previously hard-coded in `scrollbar.mixin.less`. `LoaderWrapper` (absolute fill, `rgba(255,255,255,0.6)`) and a spinning `Loader` are rendered on top when `loading` is true.
109
+ 2. **Less overrides** (`style/scrollbar.mixin.less`) — patches PerfectScrollbar's `.ps__rail-*` and `.ps__thumb-*` classes. The `.large-size` class (applied via `classnames` in `VirtualScrollbar`) activates the wide blue thumb variant.
110
+
111
+ **Token usage:**
112
+ - Default thumb: `grey-300` / `grey-500` (hover)
113
+ - Large thumb: `blue-050` / `blue-100` (hover), `grey-300` border / `grey-400` border (hover)
114
+ - Loading spinner in `Scrollbar.styles`: `grey-600`; in `DnDScrollbar.styles`: `blue-600`
115
+
116
+ The track in `DnDScrollbar` is hidden (`opacity: 0`) and revealed on parent hover via a styled-components selector chain.
117
+
118
+ ## Key dependencies
119
+
120
+ - `@ofsajd/react-perfect-scrollbar` `1.0.0` — used only by `VirtualScrollbar`; this is a fork/mirror of `react-perfect-scrollbar`
121
+ - `@synerise/ds-utils` — `useCombinedRefs`, `useResizeObserver` (used in `VirtualScrollbar`)
122
+ - `@synerise/ds-icon` + `SpinnerM` — loading spinner icon
123
+ - `@synerise/ds-core` — `useTheme` for palette tokens; peer dependency
124
+ - `classnames` — conditional class composition in `VirtualScrollbar`
125
+
126
+ ## Implementation notes
127
+
128
+ - `VirtualScrollbar` registers `transitionend`/`animationend` listeners on `document.body` (no cleanup deps array — runs after every render) to retrigger PerfectScrollbar geometry recalculation when the scrollbar becomes visible after a CSS animation.
129
+ - `DnDScrollbar` implements its own thumb-drag via `mousemove`/`mouseup`/`mouseleave` listeners on `document` and a `ResizeObserver` on the wrapper. Minimum thumb height is `48 px`; if `scrollHeight === clientHeight` (no overflow), `thumbHeight` is set to `0` (thumb hidden).
130
+ - `DnDScrollbar` does not accept `scrollbarOptions` — the prop is silently ignored because it is part of `VirtualScrollbarProps` only.
131
+ - `overscrollBehavior` is forwarded from the top-level `Scrollbar` down to whichever inner component is active. Virtual picks it up via the outer wrapper's `.ps` nested rule; DnD applies it on its own `ScrollbarContainer` directly.
132
+ - `confineScroll` behaviour differs between modes: `VirtualScrollbar` calls `event.preventDefault()` on `wheel` (blocks scroll propagation); `DnDScrollbar` calls `event.stopPropagation()` only.
133
+ - The `forwardRef` type is `HTMLElement` (broad); callers may need to cast to `HTMLDivElement` for precise access.
134
+ - The test runner is **Jest** (not Vitest) — `package.json` uses `"test": "jest"` and `jest.config.js` is present.
135
+ - `modules.d.ts` is named misleadingly — it only augments the test environment with `@testing-library/jest-dom` typings.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synerise/ds-scrollbar",
3
- "version": "1.5.0",
3
+ "version": "1.5.2",
4
4
  "description": "Scrollbar UI Component for the Synerise Design System",
5
5
  "license": "ISC",
6
6
  "repository": "Synerise/synerise-design",
@@ -17,6 +17,7 @@
17
17
  "files": [
18
18
  "/dist",
19
19
  "CHANGELOG.md",
20
+ "CLAUDE.md",
20
21
  "README.md",
21
22
  "package.json",
22
23
  "LICENSE.md"
@@ -42,8 +43,8 @@
42
43
  "types": "dist/index.d.ts",
43
44
  "dependencies": {
44
45
  "@ofsajd/react-perfect-scrollbar": "1.0.0",
45
- "@synerise/ds-icon": "^1.18.3",
46
- "@synerise/ds-utils": "^1.10.0",
46
+ "@synerise/ds-icon": "^1.18.5",
47
+ "@synerise/ds-utils": "^1.10.2",
47
48
  "classnames": "^2.3.2"
48
49
  },
49
50
  "peerDependencies": {
@@ -52,5 +53,5 @@
52
53
  "styled-components": "^5.3.3",
53
54
  "vitest": "4"
54
55
  },
55
- "gitHead": "fe3379f50afdce6d8c61a2222ebbf03324107c95"
56
+ "gitHead": "d0a43cc43d8528a36f105aceea52ab470edb71d9"
56
57
  }