@goodmanlabs/react-swipe-row 0.1.0 → 0.1.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.
- package/README.md +69 -3
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+

|
|
2
|
+

|
|
3
|
+

|
|
4
|
+
|
|
1
5
|
# react-swipe-row
|
|
2
6
|
|
|
3
7
|
A native-feeling horizontal “card rail” for React — momentum scrolling + scroll-snap alignment, with optional desktop paging controls.
|
|
@@ -16,8 +20,70 @@ This was built to solve a common UI problem: you want a horizontally scrollable
|
|
|
16
20
|
## Install
|
|
17
21
|
|
|
18
22
|
```bash
|
|
19
|
-
npm install react-swipe-row
|
|
23
|
+
npm install @goodmanlabs/react-swipe-row
|
|
20
24
|
# or
|
|
21
|
-
pnpm add react-swipe-row
|
|
25
|
+
pnpm add @goodmanlabs/react-swipe-row
|
|
22
26
|
# or
|
|
23
|
-
yarn add react-swipe-row
|
|
27
|
+
yarn add @goodmanlabs/react-swipe-row
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
import SwipeRow from '@goodmanlabs/react-swipe-row';
|
|
34
|
+
import '@goodmanlabs/react-swipe-row/style.css';
|
|
35
|
+
|
|
36
|
+
export default function Example() {
|
|
37
|
+
return (
|
|
38
|
+
<SwipeRow>
|
|
39
|
+
<div className="card">One</div>
|
|
40
|
+
<div className="card">Two</div>
|
|
41
|
+
<div className="card">Three</div>
|
|
42
|
+
</SwipeRow>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
> Note: You must import the provided CSS for base layout and snapping behavior.
|
|
47
|
+
|
|
48
|
+
## Props
|
|
49
|
+
|
|
50
|
+
| Prop | Type | Default | Description |
|
|
51
|
+
|------|------|---------|-------------|
|
|
52
|
+
| `items` | `React.ReactNode[]` | — | Optional array of React nodes to render. If provided, it takes precedence over `children`. |
|
|
53
|
+
| `children` | `React.ReactNode` | — | Content to render if `items` is not provided. |
|
|
54
|
+
| `ariaLabel` | `string` | `"Scrollable content"` | Accessible label for the scroll region (`role="region"`). |
|
|
55
|
+
| `snap` | `boolean` | `true` | Enables/disables scroll-snap behavior (applies `rsr-snap` + `rsr-snap-item`). |
|
|
56
|
+
| `pageFactor` | `number` | `0.9` | How far the controls and keyboard paging move, as a fraction of the scroller’s `clientWidth`. |
|
|
57
|
+
| `showControls` | `"auto" \| "always" \| "never"` | `"auto"` | `auto` shows controls only on “desktop-like” pointers (`(hover: hover) and (pointer: fine)`), `always` forces them on, `never` hides them. |
|
|
58
|
+
| `id` | `string` | auto-generated | Optional stable id used for `aria-controls`. If omitted, a React `useId()` value is used. |
|
|
59
|
+
| `className` | `string` | — | Extra class(es) applied to the outer wrapper (`rsr-root`). |
|
|
60
|
+
| `gapClassName` | `string` | — | Optional spacing hook applied to the scroller (e.g. Tailwind `gap-*`, or any consumer-defined class). |
|
|
61
|
+
| `classNames` | `SwipeRowClassNames` | — | Optional granular class hooks for styling without targeting internals. |
|
|
62
|
+
| `scrollerStyle` | `React.CSSProperties` | — | Inline style passthrough for the scroller. Merged after defaults (so it can override). |
|
|
63
|
+
|
|
64
|
+
### `SwipeRowClassNames`
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
type SwipeRowClassNames = {
|
|
68
|
+
root?: string; // outer wrapper
|
|
69
|
+
scroller?: string; // the horizontal scroller element
|
|
70
|
+
item?: string; // wrapper around each item (snap target)
|
|
71
|
+
controlButton?: string; // shared class for both buttons
|
|
72
|
+
prevButton?: string; // prev button
|
|
73
|
+
nextButton?: string; // next button
|
|
74
|
+
};
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Accessibility & Behavior
|
|
78
|
+
|
|
79
|
+
- The scroll container is keyboard-focusable and supports left/right arrow key paging.
|
|
80
|
+
- Paging controls use semantic `<button>` elements with accessible labels.
|
|
81
|
+
- Scroll behavior relies on native browser momentum scrolling and `scroll-snap`, not JavaScript-driven carousels.
|
|
82
|
+
- On touch-first devices, paging controls are hidden by default to avoid interfering with native gestures.
|
|
83
|
+
|
|
84
|
+
## License
|
|
85
|
+
|
|
86
|
+
MIT © Glenn Goodman
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/SwipeRow.tsx"],"sourcesContent":["import React, { useEffect, useId, useMemo, useRef, useState } from 'react';\n\nexport type ShowControlsMode = 'auto' | 'always' | 'never';\n\nexport type SwipeRowClassNames = {\n /** Outer wrapper around the scroller + controls */\n root?: string;\n /** The horizontal scroller element */\n scroller?: string;\n /** Wrapper around each item (the snap target) */\n item?: string;\n /** Shared class applied to both buttons */\n controlButton?: string;\n /** Prev button */\n prevButton?: string;\n /** Next button */\n nextButton?: string;\n};\n\nexport type SwipeRowProps = {\n /** Optional array of React nodes (alternative to children) */\n items?: React.ReactNode[];\n children?: React.ReactNode;\n\n ariaLabel?: string;\n\n /** Enable CSS scroll-snap */\n snap?: boolean;\n\n /** How far arrows / keyboard page (fraction of visible width) */\n pageFactor?: number;\n\n /** When to show left/right controls */\n showControls?: ShowControlsMode;\n\n /** Optional stable id for aria-controls */\n id?: string;\n\n /** Extra class on outer wrapper */\n className?: string;\n\n /**\n * Spacing between items.\n * This is a class hook (NOT Tailwind-specific). For non-Tailwind consumers,\n * they'll typically use the provided default CSS and ignore this.\n */\n gapClassName?: string;\n\n /** More granular class hooks */\n classNames?: SwipeRowClassNames;\n\n /**\n * Optional inline style passthrough for the scroller.\n * Useful for consumers that want scrollbarGutter, etc.\n */\n scrollerStyle?: React.CSSProperties;\n};\n\nfunction cx(...parts: Array<string | undefined | false | null>) {\n return parts.filter(Boolean).join(' ');\n}\n\nexport default function SwipeRow({\n items,\n children,\n ariaLabel = 'Scrollable content',\n className,\n gapClassName,\n snap = true,\n pageFactor = 0.9,\n showControls = 'auto',\n id,\n classNames,\n scrollerStyle,\n }: SwipeRowProps) {\n const scrollerRef = useRef<HTMLDivElement | null>(null);\n\n const [canLeft, setCanLeft] = useState(false);\n const [canRight, setCanRight] = useState(false);\n const [controlsOn, setControlsOn] = useState(showControls === 'always');\n\n const autoId = useId();\n const regionId = id ?? autoId;\n\n const content = useMemo(() => {\n if (items) return items;\n return React.Children.toArray(children);\n }, [items, children]);\n\n // Enable/disable arrows based on scroll position\n useEffect(() => {\n const el = scrollerRef.current;\n if (!el) return;\n\n const update = () => {\n // tolerance: helps avoid off-by-1 due to subpixel rounding\n const max = el.scrollWidth - el.clientWidth - 1;\n setCanLeft(el.scrollLeft > 0);\n setCanRight(el.scrollLeft < max);\n };\n\n update();\n el.addEventListener('scroll', update, { passive: true });\n window.addEventListener('resize', update);\n\n return () => {\n el.removeEventListener('scroll', update);\n window.removeEventListener('resize', update);\n };\n }, []);\n\n // Decide when to show controls (desktop-ish)\n useEffect(() => {\n if (showControls !== 'auto') {\n setControlsOn(showControls === 'always');\n return;\n }\n\n const mq = window.matchMedia('(hover: hover) and (pointer: fine)');\n const set = () => setControlsOn(mq.matches);\n\n set();\n\n // Safari fallback\n if (mq.addEventListener) mq.addEventListener('change', set);\n // eslint-disable-next-line deprecation/deprecation\n else mq.addListener?.(set);\n\n return () => {\n if (mq.removeEventListener) mq.removeEventListener('change', set);\n // eslint-disable-next-line deprecation/deprecation\n else mq.removeListener?.(set);\n };\n }, [showControls]);\n\n const page = (dir: -1 | 1) => {\n const el = scrollerRef.current;\n if (!el) return;\n const step = Math.round(el.clientWidth * pageFactor);\n el.scrollBy({ left: dir * step, behavior: 'smooth' });\n };\n\n // Keyboard paging (a11y)\n const onKeyDown = (e: React.KeyboardEvent) => {\n const el = scrollerRef.current;\n if (!el) return;\n const step = Math.round(el.clientWidth * pageFactor);\n\n if (e.key === 'ArrowRight') {\n e.preventDefault();\n el.scrollBy({ left: step, behavior: 'smooth' });\n }\n if (e.key === 'ArrowLeft') {\n e.preventDefault();\n el.scrollBy({ left: -step, behavior: 'smooth' });\n }\n };\n\n return (\n <div className={cx('rsr-root', className, classNames?.root)}>\n <div\n ref={scrollerRef}\n role=\"region\"\n aria-label={ariaLabel}\n tabIndex={0}\n onKeyDown={onKeyDown}\n className={cx(\n 'rsr-scroller',\n snap && 'rsr-snap',\n gapClassName,\n classNames?.scroller\n )}\n style={{\n WebkitOverflowScrolling: 'touch',\n // matches your original (optional, but harmless)\n scrollbarGutter: 'stable both-edges',\n ...scrollerStyle,\n }}\n id={regionId}\n >\n {content.map((node, i) => (\n <div\n key={i}\n className={cx('rsr-item', snap && 'rsr-snap-item', classNames?.item)}\n >\n {node}\n </div>\n ))}\n </div>\n\n {controlsOn && (\n <>\n <button\n type=\"button\"\n onClick={() => page(-1)}\n disabled={!canLeft}\n aria-controls={regionId}\n aria-label=\"Scroll left\"\n className={cx(\n 'rsr-control',\n 'rsr-prev',\n classNames?.controlButton,\n classNames?.prevButton\n )}\n >\n ‹\n </button>\n\n <button\n type=\"button\"\n onClick={() => page(1)}\n disabled={!canRight}\n aria-controls={regionId}\n aria-label=\"Scroll right\"\n className={cx(\n 'rsr-control',\n 'rsr-next',\n classNames?.controlButton,\n classNames?.nextButton\n )}\n >\n ›\n </button>\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["_a"]}
|
|
1
|
+
{"version":3,"sources":["../src/SwipeRow.tsx"],"sourcesContent":["'use client';\n\nimport React, { useEffect, useId, useMemo, useRef, useState } from 'react';\n\nexport type ShowControlsMode = 'auto' | 'always' | 'never';\n\nexport type SwipeRowClassNames = {\n /** Outer wrapper around the scroller + controls */\n root?: string;\n /** The horizontal scroller element */\n scroller?: string;\n /** Wrapper around each item (the snap target) */\n item?: string;\n /** Shared class applied to both buttons */\n controlButton?: string;\n /** Prev button */\n prevButton?: string;\n /** Next button */\n nextButton?: string;\n};\n\nexport type SwipeRowProps = {\n /** Optional array of React nodes (alternative to children) */\n items?: React.ReactNode[];\n children?: React.ReactNode;\n\n ariaLabel?: string;\n\n /** Enable CSS scroll-snap */\n snap?: boolean;\n\n /** How far arrows / keyboard page (fraction of visible width) */\n pageFactor?: number;\n\n /** When to show left/right controls */\n showControls?: ShowControlsMode;\n\n /** Optional stable id for aria-controls */\n id?: string;\n\n /** Extra class on outer wrapper */\n className?: string;\n\n /**\n * Spacing between items.\n * This is a class hook (NOT Tailwind-specific). For non-Tailwind consumers,\n * they'll typically use the provided default CSS and ignore this.\n */\n gapClassName?: string;\n\n /** More granular class hooks */\n classNames?: SwipeRowClassNames;\n\n /**\n * Optional inline style passthrough for the scroller.\n * Useful for consumers that want scrollbarGutter, etc.\n */\n scrollerStyle?: React.CSSProperties;\n};\n\nfunction cx(...parts: Array<string | undefined | false | null>) {\n return parts.filter(Boolean).join(' ');\n}\n\nexport default function SwipeRow({\n items,\n children,\n ariaLabel = 'Scrollable content',\n className,\n gapClassName,\n snap = true,\n pageFactor = 0.9,\n showControls = 'auto',\n id,\n classNames,\n scrollerStyle,\n }: SwipeRowProps) {\n const scrollerRef = useRef<HTMLDivElement | null>(null);\n\n const [canLeft, setCanLeft] = useState(false);\n const [canRight, setCanRight] = useState(false);\n const [controlsOn, setControlsOn] = useState(showControls === 'always');\n\n const autoId = useId();\n const regionId = id ?? autoId;\n\n const content = useMemo(() => {\n if (items) return items;\n return React.Children.toArray(children);\n }, [items, children]);\n\n // Enable/disable arrows based on scroll position\n useEffect(() => {\n const el = scrollerRef.current;\n if (!el) return;\n\n const update = () => {\n // tolerance: helps avoid off-by-1 due to subpixel rounding\n const max = el.scrollWidth - el.clientWidth - 1;\n setCanLeft(el.scrollLeft > 0);\n setCanRight(el.scrollLeft < max);\n };\n\n update();\n el.addEventListener('scroll', update, { passive: true });\n window.addEventListener('resize', update);\n\n return () => {\n el.removeEventListener('scroll', update);\n window.removeEventListener('resize', update);\n };\n }, []);\n\n // Decide when to show controls (desktop-ish)\n useEffect(() => {\n if (showControls !== 'auto') {\n setControlsOn(showControls === 'always');\n return;\n }\n\n const mq = window.matchMedia('(hover: hover) and (pointer: fine)');\n const set = () => setControlsOn(mq.matches);\n\n set();\n\n // Safari fallback\n if (mq.addEventListener) mq.addEventListener('change', set);\n // eslint-disable-next-line deprecation/deprecation\n else mq.addListener?.(set);\n\n return () => {\n if (mq.removeEventListener) mq.removeEventListener('change', set);\n // eslint-disable-next-line deprecation/deprecation\n else mq.removeListener?.(set);\n };\n }, [showControls]);\n\n const page = (dir: -1 | 1) => {\n const el = scrollerRef.current;\n if (!el) return;\n const step = Math.round(el.clientWidth * pageFactor);\n el.scrollBy({ left: dir * step, behavior: 'smooth' });\n };\n\n // Keyboard paging (a11y)\n const onKeyDown = (e: React.KeyboardEvent) => {\n const el = scrollerRef.current;\n if (!el) return;\n const step = Math.round(el.clientWidth * pageFactor);\n\n if (e.key === 'ArrowRight') {\n e.preventDefault();\n el.scrollBy({ left: step, behavior: 'smooth' });\n }\n if (e.key === 'ArrowLeft') {\n e.preventDefault();\n el.scrollBy({ left: -step, behavior: 'smooth' });\n }\n };\n\n return (\n <div className={cx('rsr-root', className, classNames?.root)}>\n <div\n ref={scrollerRef}\n role=\"region\"\n aria-label={ariaLabel}\n tabIndex={0}\n onKeyDown={onKeyDown}\n className={cx(\n 'rsr-scroller',\n snap && 'rsr-snap',\n gapClassName,\n classNames?.scroller\n )}\n style={{\n WebkitOverflowScrolling: 'touch',\n // matches your original (optional, but harmless)\n scrollbarGutter: 'stable both-edges',\n ...scrollerStyle,\n }}\n id={regionId}\n >\n {content.map((node, i) => (\n <div\n key={i}\n className={cx('rsr-item', snap && 'rsr-snap-item', classNames?.item)}\n >\n {node}\n </div>\n ))}\n </div>\n\n {controlsOn && (\n <>\n <button\n type=\"button\"\n onClick={() => page(-1)}\n disabled={!canLeft}\n aria-controls={regionId}\n aria-label=\"Scroll left\"\n className={cx(\n 'rsr-control',\n 'rsr-prev',\n classNames?.controlButton,\n classNames?.prevButton\n )}\n >\n ‹\n </button>\n\n <button\n type=\"button\"\n onClick={() => page(1)}\n disabled={!canRight}\n aria-controls={regionId}\n aria-label=\"Scroll right\"\n className={cx(\n 'rsr-control',\n 'rsr-next',\n classNames?.controlButton,\n classNames?.nextButton\n )}\n >\n ›\n </button>\n </>\n )}\n </div>\n );\n}\n"],"mappings":";;;AAEA,OAAO,SAAS,WAAW,OAAO,SAAS,QAAQ,gBAAgB;AAqL/C,SAUJ,UAVI,KAUJ,YAVI;AA3HpB,SAAS,MAAM,OAAiD;AAC5D,SAAO,MAAM,OAAO,OAAO,EAAE,KAAK,GAAG;AACzC;AAEe,SAAR,SAA0B;AAAA,EACI;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,aAAa;AAAA,EACb,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA;AACJ,GAAkB;AAC/C,QAAM,cAAc,OAA8B,IAAI;AAEtD,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAC5C,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,iBAAiB,QAAQ;AAEtE,QAAM,SAAS,MAAM;AACrB,QAAM,WAAW,kBAAM;AAEvB,QAAM,UAAU,QAAQ,MAAM;AAC1B,QAAI,MAAO,QAAO;AAClB,WAAO,MAAM,SAAS,QAAQ,QAAQ;AAAA,EAC1C,GAAG,CAAC,OAAO,QAAQ,CAAC;AAGpB,YAAU,MAAM;AACZ,UAAM,KAAK,YAAY;AACvB,QAAI,CAAC,GAAI;AAET,UAAM,SAAS,MAAM;AAEjB,YAAM,MAAM,GAAG,cAAc,GAAG,cAAc;AAC9C,iBAAW,GAAG,aAAa,CAAC;AAC5B,kBAAY,GAAG,aAAa,GAAG;AAAA,IACnC;AAEA,WAAO;AACP,OAAG,iBAAiB,UAAU,QAAQ,EAAE,SAAS,KAAK,CAAC;AACvD,WAAO,iBAAiB,UAAU,MAAM;AAExC,WAAO,MAAM;AACT,SAAG,oBAAoB,UAAU,MAAM;AACvC,aAAO,oBAAoB,UAAU,MAAM;AAAA,IAC/C;AAAA,EACJ,GAAG,CAAC,CAAC;AAGL,YAAU,MAAM;AAlHpB;AAmHQ,QAAI,iBAAiB,QAAQ;AACzB,oBAAc,iBAAiB,QAAQ;AACvC;AAAA,IACJ;AAEA,UAAM,KAAK,OAAO,WAAW,oCAAoC;AACjE,UAAM,MAAM,MAAM,cAAc,GAAG,OAAO;AAE1C,QAAI;AAGJ,QAAI,GAAG,iBAAkB,IAAG,iBAAiB,UAAU,GAAG;AAAA,QAErD,UAAG,gBAAH,4BAAiB;AAEtB,WAAO,MAAM;AAlIrB,UAAAA;AAmIY,UAAI,GAAG,oBAAqB,IAAG,oBAAoB,UAAU,GAAG;AAAA,UAE3D,EAAAA,MAAA,GAAG,mBAAH,gBAAAA,IAAA,SAAoB;AAAA,IAC7B;AAAA,EACJ,GAAG,CAAC,YAAY,CAAC;AAEjB,QAAM,OAAO,CAAC,QAAgB;AAC1B,UAAM,KAAK,YAAY;AACvB,QAAI,CAAC,GAAI;AACT,UAAM,OAAO,KAAK,MAAM,GAAG,cAAc,UAAU;AACnD,OAAG,SAAS,EAAE,MAAM,MAAM,MAAM,UAAU,SAAS,CAAC;AAAA,EACxD;AAGA,QAAM,YAAY,CAAC,MAA2B;AAC1C,UAAM,KAAK,YAAY;AACvB,QAAI,CAAC,GAAI;AACT,UAAM,OAAO,KAAK,MAAM,GAAG,cAAc,UAAU;AAEnD,QAAI,EAAE,QAAQ,cAAc;AACxB,QAAE,eAAe;AACjB,SAAG,SAAS,EAAE,MAAM,MAAM,UAAU,SAAS,CAAC;AAAA,IAClD;AACA,QAAI,EAAE,QAAQ,aAAa;AACvB,QAAE,eAAe;AACjB,SAAG,SAAS,EAAE,MAAM,CAAC,MAAM,UAAU,SAAS,CAAC;AAAA,IACnD;AAAA,EACJ;AAEA,SACI,qBAAC,SAAI,WAAW,GAAG,YAAY,WAAW,yCAAY,IAAI,GACtD;AAAA;AAAA,MAAC;AAAA;AAAA,QACG,KAAK;AAAA,QACL,MAAK;AAAA,QACL,cAAY;AAAA,QACZ,UAAU;AAAA,QACV;AAAA,QACA,WAAW;AAAA,UACP;AAAA,UACA,QAAQ;AAAA,UACR;AAAA,UACA,yCAAY;AAAA,QAChB;AAAA,QACA,OAAO;AAAA,UACH,yBAAyB;AAAA;AAAA,UAEzB,iBAAiB;AAAA,UACjB,GAAG;AAAA,QACP;AAAA,QACA,IAAI;AAAA,QAEH,kBAAQ,IAAI,CAAC,MAAM,MAChB;AAAA,UAAC;AAAA;AAAA,YAEG,WAAW,GAAG,YAAY,QAAQ,iBAAiB,yCAAY,IAAI;AAAA,YAElE;AAAA;AAAA,UAHI;AAAA,QAIT,CACH;AAAA;AAAA,IACL;AAAA,IAEC,cACG,iCACI;AAAA;AAAA,QAAC;AAAA;AAAA,UACG,MAAK;AAAA,UACL,SAAS,MAAM,KAAK,EAAE;AAAA,UACtB,UAAU,CAAC;AAAA,UACX,iBAAe;AAAA,UACf,cAAW;AAAA,UACX,WAAW;AAAA,YACP;AAAA,YACA;AAAA,YACA,yCAAY;AAAA,YACZ,yCAAY;AAAA,UAChB;AAAA,UACH;AAAA;AAAA,MAED;AAAA,MAEA;AAAA,QAAC;AAAA;AAAA,UACG,MAAK;AAAA,UACL,SAAS,MAAM,KAAK,CAAC;AAAA,UACrB,UAAU,CAAC;AAAA,UACX,iBAAe;AAAA,UACf,cAAW;AAAA,UACX,WAAW;AAAA,YACP;AAAA,YACA;AAAA,YACA,yCAAY;AAAA,YACZ,yCAAY;AAAA,UAChB;AAAA,UACH;AAAA;AAAA,MAED;AAAA,OACJ;AAAA,KAER;AAER;","names":["_a"]}
|