@present-day/reveal-row 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Present Day
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,115 @@
1
+ # `@present-day/reveal-row`
2
+
3
+ Horizontally scrollable row that reveals one or two action columns — **right**, **left**, or **both** sides. Three snap positions: left · center · right. Styling is entirely via `classNames` props (no bundled CSS). Scroll physics use inline `style` on the scroll track so the component works with zero external CSS.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm i @present-day/reveal-row
9
+ ```
10
+
11
+ Peer dependencies: `react`, `react-dom` (v18 or v19).
12
+
13
+ ## Basic usage
14
+
15
+ ```tsx
16
+ import { RevealRow } from '@present-day/reveal-row'
17
+
18
+ // Right mode — swipe left to reveal
19
+ <RevealRow right={<button onClick={handleDelete}>Delete</button>}>
20
+ <MyRowContent />
21
+ </RevealRow>
22
+
23
+ // Left mode — swipe right to reveal
24
+ <RevealRow left={<button onClick={handlePin}>Pin</button>}>
25
+ <MyRowContent />
26
+ </RevealRow>
27
+
28
+ // Both modes — swipe either direction
29
+ <RevealRow
30
+ left={<button onClick={handlePin}>Pin</button>}
31
+ right={<button onClick={handleDelete}>Delete</button>}
32
+ >
33
+ <MyRowContent />
34
+ </RevealRow>
35
+ ```
36
+
37
+ ## Modes
38
+
39
+ | `mode` | Slots used | Resting ("closed") scroll |
40
+ | ------- | -------------------- | ------------------------------------------- |
41
+ | `right` | `right` only | `scrollLeft = 0` (left edge) |
42
+ | `left` | `left` only | `scrollLeft = wL` (after leading column) |
43
+ | `both` | `left` and `right` | `scrollLeft = wL` (main fills viewport) |
44
+
45
+ Omit `mode` and it's inferred: both slots → `both`, only `left` → `left`, otherwise `right`.
46
+
47
+ ## Props
48
+
49
+ | Prop | Type | Default | Description |
50
+ | ---- | ---- | ------- | ----------- |
51
+ | `children` | `ReactNode` | — | Primary row content |
52
+ | `left` | `ReactNode` | — | Leading action column |
53
+ | `right` | `ReactNode` | — | Trailing action column |
54
+ | `mode` | `'left' \| 'right' \| 'both'` | inferred | Override mode detection |
55
+ | `actionWidthLeft` | `number` | `88` | Width (px) of the left column |
56
+ | `actionWidthRight` | `number` | `88` | Width (px) of the right column |
57
+ | `classNames` | `RevealRowClassNames` | `{}` | Class names for each sub-element |
58
+ | `showHandle` | `boolean` | `true` | Render the default 6-dot drag affordance |
59
+ | `handle` | `ReactNode` | — | Replace the default handle with custom content |
60
+ | `handlePosition` | `'start' \| 'end'` | `'start'` in left mode, `'end'` otherwise | Where the handle strip sits in the row |
61
+ | `handleTitle` | `string` | `'Drag horizontally…'` | Tooltip on the default handle |
62
+ | `handleAriaLabel` | `string` | `'Drag horizontally…'` | Screen-reader text on the default handle |
63
+ | `onRevealChange` | `(pos: RevealPosition) => void` | — | Fires when the settled position changes (debounced) |
64
+ | `onScroll` | `UIEventHandler` | — | Raw scroll events |
65
+ | `disabled` | `boolean` | `false` | Disables swiping |
66
+ | `resetWhenDisabled` | `boolean` | `true` | Snap closed when `disabled` becomes true |
67
+ | `isActive` | `boolean` | `false` | Snap closed (e.g. another row was selected) |
68
+ | `className` | `string` | — | Added to the root scroll element |
69
+ | `style` | `CSSProperties` | — | Added to the root scroll element |
70
+
71
+ ## Ref API
72
+
73
+ ```tsx
74
+ const ref = useRef<RevealRowHandle>(null)
75
+
76
+ <RevealRow ref={ref} right={<DeleteButton />}>
77
+ <MyRowContent />
78
+ </RevealRow>
79
+
80
+ // Programmatic control
81
+ ref.current?.close() // snap to center
82
+ ref.current?.reveal('left') // snap to left action
83
+ ref.current?.reveal('right') // snap to right action
84
+ ref.current?.reveal('center') // alias for close
85
+ ```
86
+
87
+ ## classNames
88
+
89
+ All sub-elements accept class names for styling:
90
+
91
+ ```tsx
92
+ <RevealRow
93
+ classNames={{
94
+ root: 'my-row', // scroll container
95
+ main: 'my-row__main', // main content track
96
+ mainInner: '', // flex wrapper inside main
97
+ left: 'my-row__left', // left action column
98
+ right: 'my-row__right', // right action column
99
+ handleContainer: '', // outer handle strip
100
+ handleIcon: '', // icon wrapper inside handle
101
+ }}
102
+ >
103
+ ```
104
+
105
+ ## Integration notes
106
+
107
+ **Nested vertical scroll** — the root uses `touch-action: pan-x pan-y` so a parent list can still scroll vertically.
108
+
109
+ **Overscroll / browser back gestures** — if horizontal swipes compete with iOS back or macOS trackpad history navigation, set `overscroll-behavior-x: none` on a suitable ancestor (e.g. a full-screen container).
110
+
111
+ **Preventing row activation on swipe** — the component guards against triggering a click after a horizontal drag. If you wrap the row in a command palette item or similar, keep the built-in `onClickCapture` behaviour intact, or replicate it.
112
+
113
+ ## Publishing
114
+
115
+ Run `bun run build` to produce `dist/`. The `exports` field in `package.json` points to `dist/index.{js,mjs,d.ts}`. Push a tag to trigger the GitHub Actions publish workflow (OIDC trusted publishing — no npm token needed in secrets).
@@ -0,0 +1,89 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, UIEvent, CSSProperties } from 'react';
3
+
4
+ declare const REVEAL_MODE: {
5
+ readonly left: "left";
6
+ readonly right: "right";
7
+ readonly both: "both";
8
+ };
9
+ type RevealMode = (typeof REVEAL_MODE)[keyof typeof REVEAL_MODE];
10
+ declare const REVEAL_POSITION: {
11
+ readonly left: "left";
12
+ readonly center: "center";
13
+ readonly right: "right";
14
+ };
15
+ type RevealPosition = (typeof REVEAL_POSITION)[keyof typeof REVEAL_POSITION];
16
+ declare const REVEAL_HANDLE_POSITION: {
17
+ readonly start: "start";
18
+ readonly end: "end";
19
+ };
20
+ type RevealHandlePosition = (typeof REVEAL_HANDLE_POSITION)[keyof typeof REVEAL_HANDLE_POSITION];
21
+ type RevealRowClassNames = {
22
+ root?: string;
23
+ main?: string;
24
+ mainInner?: string;
25
+ left?: string;
26
+ right?: string;
27
+ handleContainer?: string;
28
+ handleIcon?: string;
29
+ };
30
+ type RevealRowHandle = {
31
+ close: () => void;
32
+ reveal: (position: RevealPosition) => void;
33
+ };
34
+ type RevealRowProps = {
35
+ /** Primary row content (e.g. title, subtitle). */
36
+ children: ReactNode;
37
+ /** Optional leading action column (e.g. delete). Omitted in `right` mode. */
38
+ left?: ReactNode;
39
+ /**
40
+ * Trailing action column (e.g. add). Omitted in `left` mode.
41
+ */
42
+ right?: ReactNode;
43
+ /**
44
+ * If both `left` and `right` are set, defaults to `both`. If only one side,
45
+ * mode is derived unless overridden.
46
+ */
47
+ mode?: RevealMode;
48
+ actionWidthLeft?: number;
49
+ actionWidthRight?: number;
50
+ classNames?: RevealRowClassNames;
51
+ /** @default true */
52
+ showHandle?: boolean;
53
+ /** Replaces the built-in drag affordance. */
54
+ handle?: ReactNode;
55
+ /**
56
+ * Where the handle sits in the main row. Default: `start` in `left` mode,
57
+ * `end` otherwise.
58
+ */
59
+ handlePosition?: RevealHandlePosition;
60
+ handleTitle?: string;
61
+ /** Shown in screen-reader text when using the default drag handle. */
62
+ handleAriaLabel?: string;
63
+ /** Fires when the settled position changes. */
64
+ onRevealChange?: (position: RevealPosition) => void;
65
+ onScroll?: (event: UIEvent<HTMLDivElement>) => void;
66
+ /** Resets to closed; default true. */
67
+ resetWhenDisabled?: boolean;
68
+ disabled?: boolean;
69
+ /**
70
+ * When true, snaps back to the closed position (e.g. row became active/selected
71
+ * elsewhere in the list).
72
+ */
73
+ isActive?: boolean;
74
+ className?: string;
75
+ style?: CSSProperties;
76
+ };
77
+
78
+ /**
79
+ * Map horizontal scroll position to a logical "revealed" state.
80
+ * `wL` / `wR` are the fixed action column widths; `maxScroll` is
81
+ * `Math.max(0, scrollWidth - clientWidth)`.
82
+ */
83
+ declare function getRevealFromScroll(scrollLeft: number, maxScroll: number, wL: number, wR: number, mode: RevealMode): RevealPosition;
84
+ /** "Closed" / resting scrollLeft for a mode (main panel only). */
85
+ declare function getScrollClosed(wL: number, _wR: number, _maxScroll: number, mode: RevealMode): number;
86
+
87
+ declare const RevealRow: react.ForwardRefExoticComponent<RevealRowProps & react.RefAttributes<RevealRowHandle>>;
88
+
89
+ export { REVEAL_HANDLE_POSITION, REVEAL_MODE, REVEAL_POSITION, type RevealHandlePosition, type RevealMode, type RevealPosition, RevealRow, type RevealRowClassNames, type RevealRowHandle, type RevealRowProps, getRevealFromScroll, getScrollClosed };
@@ -0,0 +1,89 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, UIEvent, CSSProperties } from 'react';
3
+
4
+ declare const REVEAL_MODE: {
5
+ readonly left: "left";
6
+ readonly right: "right";
7
+ readonly both: "both";
8
+ };
9
+ type RevealMode = (typeof REVEAL_MODE)[keyof typeof REVEAL_MODE];
10
+ declare const REVEAL_POSITION: {
11
+ readonly left: "left";
12
+ readonly center: "center";
13
+ readonly right: "right";
14
+ };
15
+ type RevealPosition = (typeof REVEAL_POSITION)[keyof typeof REVEAL_POSITION];
16
+ declare const REVEAL_HANDLE_POSITION: {
17
+ readonly start: "start";
18
+ readonly end: "end";
19
+ };
20
+ type RevealHandlePosition = (typeof REVEAL_HANDLE_POSITION)[keyof typeof REVEAL_HANDLE_POSITION];
21
+ type RevealRowClassNames = {
22
+ root?: string;
23
+ main?: string;
24
+ mainInner?: string;
25
+ left?: string;
26
+ right?: string;
27
+ handleContainer?: string;
28
+ handleIcon?: string;
29
+ };
30
+ type RevealRowHandle = {
31
+ close: () => void;
32
+ reveal: (position: RevealPosition) => void;
33
+ };
34
+ type RevealRowProps = {
35
+ /** Primary row content (e.g. title, subtitle). */
36
+ children: ReactNode;
37
+ /** Optional leading action column (e.g. delete). Omitted in `right` mode. */
38
+ left?: ReactNode;
39
+ /**
40
+ * Trailing action column (e.g. add). Omitted in `left` mode.
41
+ */
42
+ right?: ReactNode;
43
+ /**
44
+ * If both `left` and `right` are set, defaults to `both`. If only one side,
45
+ * mode is derived unless overridden.
46
+ */
47
+ mode?: RevealMode;
48
+ actionWidthLeft?: number;
49
+ actionWidthRight?: number;
50
+ classNames?: RevealRowClassNames;
51
+ /** @default true */
52
+ showHandle?: boolean;
53
+ /** Replaces the built-in drag affordance. */
54
+ handle?: ReactNode;
55
+ /**
56
+ * Where the handle sits in the main row. Default: `start` in `left` mode,
57
+ * `end` otherwise.
58
+ */
59
+ handlePosition?: RevealHandlePosition;
60
+ handleTitle?: string;
61
+ /** Shown in screen-reader text when using the default drag handle. */
62
+ handleAriaLabel?: string;
63
+ /** Fires when the settled position changes. */
64
+ onRevealChange?: (position: RevealPosition) => void;
65
+ onScroll?: (event: UIEvent<HTMLDivElement>) => void;
66
+ /** Resets to closed; default true. */
67
+ resetWhenDisabled?: boolean;
68
+ disabled?: boolean;
69
+ /**
70
+ * When true, snaps back to the closed position (e.g. row became active/selected
71
+ * elsewhere in the list).
72
+ */
73
+ isActive?: boolean;
74
+ className?: string;
75
+ style?: CSSProperties;
76
+ };
77
+
78
+ /**
79
+ * Map horizontal scroll position to a logical "revealed" state.
80
+ * `wL` / `wR` are the fixed action column widths; `maxScroll` is
81
+ * `Math.max(0, scrollWidth - clientWidth)`.
82
+ */
83
+ declare function getRevealFromScroll(scrollLeft: number, maxScroll: number, wL: number, wR: number, mode: RevealMode): RevealPosition;
84
+ /** "Closed" / resting scrollLeft for a mode (main panel only). */
85
+ declare function getScrollClosed(wL: number, _wR: number, _maxScroll: number, mode: RevealMode): number;
86
+
87
+ declare const RevealRow: react.ForwardRefExoticComponent<RevealRowProps & react.RefAttributes<RevealRowHandle>>;
88
+
89
+ export { REVEAL_HANDLE_POSITION, REVEAL_MODE, REVEAL_POSITION, type RevealHandlePosition, type RevealMode, type RevealPosition, RevealRow, type RevealRowClassNames, type RevealRowHandle, type RevealRowProps, getRevealFromScroll, getScrollClosed };
package/dist/index.js ADDED
@@ -0,0 +1,390 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ REVEAL_HANDLE_POSITION: () => REVEAL_HANDLE_POSITION,
24
+ REVEAL_MODE: () => REVEAL_MODE,
25
+ REVEAL_POSITION: () => REVEAL_POSITION,
26
+ RevealRow: () => RevealRow,
27
+ getRevealFromScroll: () => getRevealFromScroll,
28
+ getScrollClosed: () => getScrollClosed
29
+ });
30
+ module.exports = __toCommonJS(index_exports);
31
+
32
+ // src/types.ts
33
+ var REVEAL_MODE = {
34
+ left: "left",
35
+ right: "right",
36
+ both: "both"
37
+ };
38
+ var REVEAL_POSITION = {
39
+ left: "left",
40
+ center: "center",
41
+ right: "right"
42
+ };
43
+ var REVEAL_HANDLE_POSITION = {
44
+ start: "start",
45
+ end: "end"
46
+ };
47
+
48
+ // src/getRevealFromScroll.ts
49
+ function getRevealFromScroll(scrollLeft, maxScroll, wL, wR, mode) {
50
+ if (mode === REVEAL_MODE.right) {
51
+ if (maxScroll <= 0) return REVEAL_POSITION.center;
52
+ return scrollLeft < wR / 2 ? REVEAL_POSITION.center : REVEAL_POSITION.right;
53
+ }
54
+ if (mode === REVEAL_MODE.left) {
55
+ if (maxScroll <= 0) return REVEAL_POSITION.center;
56
+ return scrollLeft < wL / 2 ? REVEAL_POSITION.left : REVEAL_POSITION.center;
57
+ }
58
+ if (maxScroll <= 0) return REVEAL_POSITION.center;
59
+ if (scrollLeft < wL / 2) return REVEAL_POSITION.left;
60
+ if (scrollLeft > maxScroll - wR / 2) return REVEAL_POSITION.right;
61
+ return REVEAL_POSITION.center;
62
+ }
63
+ function getScrollClosed(wL, _wR, _maxScroll, mode) {
64
+ if (mode === REVEAL_MODE.right) return 0;
65
+ return wL;
66
+ }
67
+
68
+ // src/RevealRow.tsx
69
+ var import_react = require("react");
70
+
71
+ // src/DefaultHandleIcon.tsx
72
+ var import_jsx_runtime = require("react/jsx-runtime");
73
+ function DefaultHandleIcon() {
74
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
75
+ "svg",
76
+ {
77
+ width: 16,
78
+ height: 16,
79
+ viewBox: "0 0 16 16",
80
+ fill: "currentColor",
81
+ "aria-hidden": true,
82
+ children: [
83
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "6", cy: "4", r: "1.2" }),
84
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "10", cy: "4", r: "1.2" }),
85
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "6", cy: "8", r: "1.2" }),
86
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "10", cy: "8", r: "1.2" }),
87
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "6", cy: "12", r: "1.2" }),
88
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "10", cy: "12", r: "1.2" })
89
+ ]
90
+ }
91
+ );
92
+ }
93
+
94
+ // src/RevealRow.tsx
95
+ var import_jsx_runtime2 = require("react/jsx-runtime");
96
+ function cx(...parts) {
97
+ return parts.filter(Boolean).join(" ");
98
+ }
99
+ function getMaxScroll(el) {
100
+ return Math.max(0, el.scrollWidth - el.clientWidth);
101
+ }
102
+ function resolveMode(left, right, mode) {
103
+ if (mode) return mode;
104
+ if (left != null && right != null) return REVEAL_MODE.both;
105
+ if (left != null) return REVEAL_MODE.left;
106
+ return REVEAL_MODE.right;
107
+ }
108
+ var SCROLL_REVEAL_DEBOUNCE_MS = 64;
109
+ var restEpsilon = 2;
110
+ var rootScrollStyle = {
111
+ display: "grid",
112
+ overflowX: "auto",
113
+ scrollSnapType: "x mandatory",
114
+ scrollbarWidth: "none",
115
+ WebkitOverflowScrolling: "touch",
116
+ overscrollBehaviorX: "contain",
117
+ touchAction: "pan-x pan-y"
118
+ };
119
+ var snapStart = { scrollSnapAlign: "start" };
120
+ var snapEnd = { scrollSnapAlign: "end" };
121
+ function RevealRowInner({
122
+ children,
123
+ left,
124
+ right,
125
+ mode: modeProp,
126
+ actionWidthLeft: wLIn,
127
+ actionWidthRight: wRIn,
128
+ classNames = {},
129
+ showHandle = true,
130
+ handle: handleOverride,
131
+ handlePosition: handlePositionProp,
132
+ handleTitle = "Drag horizontally to show actions",
133
+ handleAriaLabel = "Drag horizontally to show actions",
134
+ onRevealChange,
135
+ onScroll: onScrollProp,
136
+ resetWhenDisabled = true,
137
+ disabled = false,
138
+ isActive = false,
139
+ className,
140
+ style,
141
+ forwardedRef
142
+ }) {
143
+ const mode = resolveMode(left, right, modeProp);
144
+ const hasL = mode === REVEAL_MODE.left || mode === REVEAL_MODE.both;
145
+ const hasR = mode === REVEAL_MODE.right || mode === REVEAL_MODE.both;
146
+ const wL = hasL ? wLIn ?? 88 : 0;
147
+ const wR = hasR ? wRIn ?? 88 : 0;
148
+ const handlePosition = handlePositionProp ?? (mode === REVEAL_MODE.left ? REVEAL_HANDLE_POSITION.start : REVEAL_HANDLE_POSITION.end);
149
+ const containerRef = (0, import_react.useRef)(null);
150
+ const leftRef = (0, import_react.useRef)(null);
151
+ const rightRef = (0, import_react.useRef)(null);
152
+ const lastEmitted = (0, import_react.useRef)(null);
153
+ const swipedRef = (0, import_react.useRef)(false);
154
+ const debounceTimer = (0, import_react.useRef)(null);
155
+ const hasAppliedInitialScroll = (0, import_react.useRef)(false);
156
+ const readPosition = (0, import_react.useCallback)(() => {
157
+ const el = containerRef.current;
158
+ if (!el) return REVEAL_POSITION.center;
159
+ return getRevealFromScroll(el.scrollLeft, getMaxScroll(el), wL, wR, mode);
160
+ }, [wL, wR, mode]);
161
+ const emitReveal = (0, import_react.useCallback)(
162
+ (position) => {
163
+ if (lastEmitted.current === position) return;
164
+ lastEmitted.current = position;
165
+ onRevealChange?.(position);
166
+ },
167
+ [onRevealChange]
168
+ );
169
+ const scrollToClosed = (0, import_react.useCallback)(() => {
170
+ const el = containerRef.current;
171
+ if (!el) return;
172
+ const m = getMaxScroll(el);
173
+ const closed = getScrollClosed(wL, wR, m, mode);
174
+ el.scrollLeft = closed;
175
+ }, [wL, wR, mode]);
176
+ (0, import_react.useImperativeHandle)(
177
+ forwardedRef,
178
+ () => ({
179
+ close: () => {
180
+ scrollToClosed();
181
+ const p = readPosition();
182
+ if (lastEmitted.current !== p) {
183
+ lastEmitted.current = p;
184
+ onRevealChange?.(p);
185
+ }
186
+ },
187
+ reveal: (position) => {
188
+ const el = containerRef.current;
189
+ if (!el) return;
190
+ const max = getMaxScroll(el);
191
+ if (position === REVEAL_POSITION.center) {
192
+ el.scrollLeft = getScrollClosed(wL, wR, max, mode);
193
+ } else if (position === REVEAL_POSITION.left && (mode === REVEAL_MODE.left || mode === REVEAL_MODE.both)) {
194
+ el.scrollLeft = 0;
195
+ } else if (position === REVEAL_POSITION.right && (mode === REVEAL_MODE.right || mode === REVEAL_MODE.both)) {
196
+ el.scrollLeft = max;
197
+ } else {
198
+ return;
199
+ }
200
+ const p = getRevealFromScroll(el.scrollLeft, max, wL, wR, mode);
201
+ lastEmitted.current = p;
202
+ onRevealChange?.(p);
203
+ }
204
+ }),
205
+ [mode, onRevealChange, readPosition, scrollToClosed, wL, wR]
206
+ );
207
+ (0, import_react.useLayoutEffect)(() => {
208
+ if (mode === REVEAL_MODE.right) {
209
+ return;
210
+ }
211
+ const el = containerRef.current;
212
+ if (!el) return;
213
+ if (!hasAppliedInitialScroll.current) {
214
+ el.scrollLeft = wL;
215
+ hasAppliedInitialScroll.current = true;
216
+ }
217
+ }, [mode, wL]);
218
+ (0, import_react.useEffect)(() => {
219
+ if (disabled && resetWhenDisabled) {
220
+ hasAppliedInitialScroll.current = true;
221
+ scrollToClosed();
222
+ emitReveal(readPosition());
223
+ }
224
+ }, [disabled, emitReveal, readPosition, resetWhenDisabled, scrollToClosed]);
225
+ (0, import_react.useEffect)(() => {
226
+ if (isActive) {
227
+ hasAppliedInitialScroll.current = true;
228
+ scrollToClosed();
229
+ emitReveal(readPosition());
230
+ }
231
+ }, [emitReveal, isActive, readPosition, scrollToClosed]);
232
+ (0, import_react.useEffect)(
233
+ () => () => {
234
+ if (debounceTimer.current) {
235
+ clearTimeout(debounceTimer.current);
236
+ }
237
+ },
238
+ []
239
+ );
240
+ const handleScroll = (0, import_react.useCallback)(
241
+ (e) => {
242
+ onScrollProp?.(e);
243
+ const el = containerRef.current;
244
+ if (!el) return;
245
+ const m = getMaxScroll(el);
246
+ const pos = getRevealFromScroll(el.scrollLeft, m, wL, wR, mode);
247
+ const closed = getScrollClosed(wL, wR, m, mode);
248
+ if (Math.abs(el.scrollLeft - closed) > restEpsilon) {
249
+ swipedRef.current = true;
250
+ }
251
+ if (debounceTimer.current) {
252
+ clearTimeout(debounceTimer.current);
253
+ }
254
+ debounceTimer.current = setTimeout(() => {
255
+ debounceTimer.current = null;
256
+ emitReveal(pos);
257
+ }, SCROLL_REVEAL_DEBOUNCE_MS);
258
+ },
259
+ [emitReveal, mode, onScrollProp, wL, wR]
260
+ );
261
+ const onClickCapture = (0, import_react.useCallback)((e) => {
262
+ const t = e.target;
263
+ if (t) {
264
+ if (leftRef.current?.contains(t) || rightRef.current?.contains(t)) {
265
+ return;
266
+ }
267
+ }
268
+ if (swipedRef.current) {
269
+ e.preventDefault();
270
+ e.stopPropagation();
271
+ swipedRef.current = false;
272
+ }
273
+ }, []);
274
+ const gridTemplateColumns = mode === REVEAL_MODE.right ? `100% ${wR}px` : mode === REVEAL_MODE.left ? `${wL}px 100%` : `${wL}px 100% ${wR}px`;
275
+ const handleContent = showHandle ? handleOverride !== void 0 ? handleOverride : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
276
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
277
+ "span",
278
+ {
279
+ className: "sr-only",
280
+ style: {
281
+ position: "absolute",
282
+ width: 1,
283
+ height: 1,
284
+ padding: 0,
285
+ margin: -1,
286
+ overflow: "hidden",
287
+ clip: "rect(0,0,0,0)",
288
+ border: 0
289
+ },
290
+ children: handleAriaLabel
291
+ }
292
+ ),
293
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: classNames.handleIcon, "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(DefaultHandleIcon, {}) })
294
+ ] }) : null;
295
+ const handleStrip = showHandle ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
296
+ "div",
297
+ {
298
+ role: "presentation",
299
+ className: classNames.handleContainer,
300
+ "data-reveal-row-handle": true,
301
+ style: { flexShrink: 0, alignSelf: "stretch", display: "flex", alignItems: "center" },
302
+ title: handleOverride === void 0 ? handleTitle : void 0,
303
+ children: handleContent
304
+ }
305
+ ) : null;
306
+ const mainColumn = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
307
+ "div",
308
+ {
309
+ className: classNames.main,
310
+ "data-reveal-row-main": true,
311
+ style: {
312
+ ...snapStart,
313
+ minWidth: 0,
314
+ width: "100%",
315
+ maxWidth: "100%",
316
+ boxSizing: "border-box"
317
+ },
318
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
319
+ "div",
320
+ {
321
+ className: classNames.mainInner,
322
+ style: {
323
+ display: "flex",
324
+ minWidth: 0,
325
+ width: "100%",
326
+ alignItems: "flex-start"
327
+ },
328
+ children: [
329
+ handlePosition === REVEAL_HANDLE_POSITION.start && handleStrip,
330
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { minWidth: 0, flex: 1, flexBasis: 0 }, children }),
331
+ handlePosition === REVEAL_HANDLE_POSITION.end && handleStrip
332
+ ]
333
+ }
334
+ )
335
+ }
336
+ );
337
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
338
+ "div",
339
+ {
340
+ ref: containerRef,
341
+ "data-reveal-mode": mode,
342
+ className: cx(classNames.root, className),
343
+ onScroll: handleScroll,
344
+ onClickCapture,
345
+ style: {
346
+ ...rootScrollStyle,
347
+ ...style,
348
+ gridTemplateColumns
349
+ },
350
+ children: [
351
+ hasL ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
352
+ "div",
353
+ {
354
+ ref: leftRef,
355
+ className: classNames.left,
356
+ "data-reveal-row-left": true,
357
+ style: { ...snapStart, minWidth: 0, width: wL },
358
+ children: left
359
+ }
360
+ ) : null,
361
+ mainColumn,
362
+ hasR ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
363
+ "div",
364
+ {
365
+ ref: rightRef,
366
+ className: classNames.right,
367
+ "data-reveal-row-right": true,
368
+ style: { ...snapEnd, minWidth: 0, width: wR },
369
+ children: right
370
+ }
371
+ ) : null
372
+ ]
373
+ }
374
+ );
375
+ }
376
+ var RevealRow = (0, import_react.forwardRef)(
377
+ function RevealRow2(props, ref) {
378
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RevealRowInner, { ...props, forwardedRef: ref });
379
+ }
380
+ );
381
+ // Annotate the CommonJS export names for ESM import in node:
382
+ 0 && (module.exports = {
383
+ REVEAL_HANDLE_POSITION,
384
+ REVEAL_MODE,
385
+ REVEAL_POSITION,
386
+ RevealRow,
387
+ getRevealFromScroll,
388
+ getScrollClosed
389
+ });
390
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/types.ts","../src/getRevealFromScroll.ts","../src/RevealRow.tsx","../src/DefaultHandleIcon.tsx"],"sourcesContent":["export { getRevealFromScroll, getScrollClosed } from './getRevealFromScroll'\nexport { RevealRow } from './RevealRow'\nexport type {\n RevealHandlePosition,\n RevealMode,\n RevealPosition,\n RevealRowClassNames,\n RevealRowHandle,\n RevealRowProps,\n} from './types'\nexport {\n REVEAL_HANDLE_POSITION,\n REVEAL_MODE,\n REVEAL_POSITION,\n} from './types'\n","import type { CSSProperties, ReactNode, UIEvent } from 'react'\n\nexport const REVEAL_MODE = {\n left: 'left',\n right: 'right',\n both: 'both',\n} as const\n\nexport type RevealMode = (typeof REVEAL_MODE)[keyof typeof REVEAL_MODE]\n\nexport const REVEAL_POSITION = {\n left: 'left',\n center: 'center',\n right: 'right',\n} as const\n\nexport type RevealPosition =\n (typeof REVEAL_POSITION)[keyof typeof REVEAL_POSITION]\n\nexport const REVEAL_HANDLE_POSITION = {\n start: 'start',\n end: 'end',\n} as const\n\nexport type RevealHandlePosition =\n (typeof REVEAL_HANDLE_POSITION)[keyof typeof REVEAL_HANDLE_POSITION]\n\nexport type RevealRowClassNames = {\n root?: string\n main?: string\n mainInner?: string\n left?: string\n right?: string\n handleContainer?: string\n handleIcon?: string\n}\n\nexport type RevealRowHandle = {\n close: () => void\n reveal: (position: RevealPosition) => void\n}\n\nexport type RevealRowProps = {\n /** Primary row content (e.g. title, subtitle). */\n children: ReactNode\n /** Optional leading action column (e.g. delete). Omitted in `right` mode. */\n left?: ReactNode\n /**\n * Trailing action column (e.g. add). Omitted in `left` mode.\n */\n right?: ReactNode\n /**\n * If both `left` and `right` are set, defaults to `both`. If only one side,\n * mode is derived unless overridden.\n */\n mode?: RevealMode\n actionWidthLeft?: number\n actionWidthRight?: number\n classNames?: RevealRowClassNames\n /** @default true */\n showHandle?: boolean\n /** Replaces the built-in drag affordance. */\n handle?: ReactNode\n /**\n * Where the handle sits in the main row. Default: `start` in `left` mode,\n * `end` otherwise.\n */\n handlePosition?: RevealHandlePosition\n handleTitle?: string\n /** Shown in screen-reader text when using the default drag handle. */\n handleAriaLabel?: string\n /** Fires when the settled position changes. */\n onRevealChange?: (position: RevealPosition) => void\n onScroll?: (event: UIEvent<HTMLDivElement>) => void\n /** Resets to closed; default true. */\n resetWhenDisabled?: boolean\n disabled?: boolean\n /**\n * When true, snaps back to the closed position (e.g. row became active/selected\n * elsewhere in the list).\n */\n isActive?: boolean\n className?: string\n style?: CSSProperties\n}\n","import {\n REVEAL_MODE,\n REVEAL_POSITION,\n type RevealMode,\n type RevealPosition,\n} from './types'\n\n/**\n * Map horizontal scroll position to a logical \"revealed\" state.\n * `wL` / `wR` are the fixed action column widths; `maxScroll` is\n * `Math.max(0, scrollWidth - clientWidth)`.\n */\nexport function getRevealFromScroll(\n scrollLeft: number,\n maxScroll: number,\n wL: number,\n wR: number,\n mode: RevealMode,\n): RevealPosition {\n if (mode === REVEAL_MODE.right) {\n if (maxScroll <= 0) return REVEAL_POSITION.center\n return scrollLeft < wR / 2 ? REVEAL_POSITION.center : REVEAL_POSITION.right\n }\n if (mode === REVEAL_MODE.left) {\n if (maxScroll <= 0) return REVEAL_POSITION.center\n return scrollLeft < wL / 2 ? REVEAL_POSITION.left : REVEAL_POSITION.center\n }\n if (maxScroll <= 0) return REVEAL_POSITION.center\n if (scrollLeft < wL / 2) return REVEAL_POSITION.left\n if (scrollLeft > maxScroll - wR / 2) return REVEAL_POSITION.right\n return REVEAL_POSITION.center\n}\n\n/** \"Closed\" / resting scrollLeft for a mode (main panel only). */\nexport function getScrollClosed(\n wL: number,\n _wR: number,\n _maxScroll: number,\n mode: RevealMode,\n): number {\n if (mode === REVEAL_MODE.right) return 0\n return wL\n}\n","'use client'\n\nimport type { CSSProperties, ForwardedRef, MouseEvent, UIEvent } from 'react'\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useLayoutEffect,\n useRef,\n} from 'react'\n\nimport { DefaultHandleIcon } from './DefaultHandleIcon'\nimport { getRevealFromScroll, getScrollClosed } from './getRevealFromScroll'\nimport {\n REVEAL_HANDLE_POSITION,\n REVEAL_MODE,\n REVEAL_POSITION,\n type RevealHandlePosition,\n type RevealMode,\n type RevealPosition,\n type RevealRowClassNames,\n type RevealRowHandle,\n type RevealRowProps,\n} from './types'\n\nfunction cx(...parts: (string | undefined)[]) {\n return parts.filter(Boolean).join(' ')\n}\n\nfunction getMaxScroll(el: HTMLDivElement) {\n return Math.max(0, el.scrollWidth - el.clientWidth)\n}\n\nfunction resolveMode(\n left: RevealRowProps['left'],\n right: RevealRowProps['right'],\n mode: RevealMode | undefined,\n): RevealMode {\n if (mode) return mode\n if (left != null && right != null) return REVEAL_MODE.both\n if (left != null) return REVEAL_MODE.left\n return REVEAL_MODE.right\n}\n\nconst SCROLL_REVEAL_DEBOUNCE_MS = 64\nconst restEpsilon = 2\n\nconst rootScrollStyle: CSSProperties = {\n display: 'grid',\n overflowX: 'auto',\n scrollSnapType: 'x mandatory',\n scrollbarWidth: 'none',\n WebkitOverflowScrolling: 'touch',\n overscrollBehaviorX: 'contain',\n touchAction: 'pan-x pan-y',\n}\n\nconst snapStart: CSSProperties = { scrollSnapAlign: 'start' }\nconst snapEnd: CSSProperties = { scrollSnapAlign: 'end' }\n\ntype InnerProps = RevealRowProps & {\n forwardedRef: ForwardedRef<RevealRowHandle>\n}\n\nfunction RevealRowInner({\n children,\n left,\n right,\n mode: modeProp,\n actionWidthLeft: wLIn,\n actionWidthRight: wRIn,\n classNames = {} as RevealRowClassNames,\n showHandle = true,\n handle: handleOverride,\n handlePosition: handlePositionProp,\n handleTitle = 'Drag horizontally to show actions',\n handleAriaLabel = 'Drag horizontally to show actions',\n onRevealChange,\n onScroll: onScrollProp,\n resetWhenDisabled = true,\n disabled = false,\n isActive = false,\n className,\n style,\n forwardedRef,\n}: InnerProps) {\n const mode = resolveMode(left, right, modeProp)\n const hasL = mode === REVEAL_MODE.left || mode === REVEAL_MODE.both\n const hasR = mode === REVEAL_MODE.right || mode === REVEAL_MODE.both\n const wL = hasL ? (wLIn ?? 88) : 0\n const wR = hasR ? (wRIn ?? 88) : 0\n\n const handlePosition: RevealHandlePosition =\n handlePositionProp ??\n (mode === REVEAL_MODE.left\n ? REVEAL_HANDLE_POSITION.start\n : REVEAL_HANDLE_POSITION.end)\n\n const containerRef = useRef<HTMLDivElement>(null)\n const leftRef = useRef<HTMLDivElement>(null)\n const rightRef = useRef<HTMLDivElement>(null)\n const lastEmitted = useRef<RevealPosition | null>(null)\n const swipedRef = useRef(false)\n const debounceTimer = useRef<ReturnType<typeof setTimeout> | null>(null)\n const hasAppliedInitialScroll = useRef(false)\n\n const readPosition = useCallback((): RevealPosition => {\n const el = containerRef.current\n if (!el) return REVEAL_POSITION.center\n return getRevealFromScroll(el.scrollLeft, getMaxScroll(el), wL, wR, mode)\n }, [wL, wR, mode])\n\n const emitReveal = useCallback(\n (position: RevealPosition) => {\n if (lastEmitted.current === position) return\n lastEmitted.current = position\n onRevealChange?.(position)\n },\n [onRevealChange],\n )\n\n const scrollToClosed = useCallback(() => {\n const el = containerRef.current\n if (!el) return\n const m = getMaxScroll(el)\n const closed = getScrollClosed(wL, wR, m, mode)\n el.scrollLeft = closed\n }, [wL, wR, mode])\n\n useImperativeHandle(\n forwardedRef,\n () => ({\n close: () => {\n scrollToClosed()\n const p = readPosition()\n if (lastEmitted.current !== p) {\n lastEmitted.current = p\n onRevealChange?.(p)\n }\n },\n reveal: (position: RevealPosition) => {\n const el = containerRef.current\n if (!el) return\n const max = getMaxScroll(el)\n if (position === REVEAL_POSITION.center) {\n el.scrollLeft = getScrollClosed(wL, wR, max, mode)\n } else if (\n position === REVEAL_POSITION.left &&\n (mode === REVEAL_MODE.left || mode === REVEAL_MODE.both)\n ) {\n el.scrollLeft = 0\n } else if (\n position === REVEAL_POSITION.right &&\n (mode === REVEAL_MODE.right || mode === REVEAL_MODE.both)\n ) {\n el.scrollLeft = max\n } else {\n return\n }\n const p = getRevealFromScroll(el.scrollLeft, max, wL, wR, mode)\n lastEmitted.current = p\n onRevealChange?.(p)\n },\n }),\n [mode, onRevealChange, readPosition, scrollToClosed, wL, wR],\n )\n\n useLayoutEffect(() => {\n if (mode === REVEAL_MODE.right) {\n return\n }\n const el = containerRef.current\n if (!el) return\n if (!hasAppliedInitialScroll.current) {\n el.scrollLeft = wL\n hasAppliedInitialScroll.current = true\n }\n }, [mode, wL])\n\n useEffect(() => {\n if (disabled && resetWhenDisabled) {\n hasAppliedInitialScroll.current = true\n scrollToClosed()\n emitReveal(readPosition())\n }\n }, [disabled, emitReveal, readPosition, resetWhenDisabled, scrollToClosed])\n\n useEffect(() => {\n if (isActive) {\n hasAppliedInitialScroll.current = true\n scrollToClosed()\n emitReveal(readPosition())\n }\n }, [emitReveal, isActive, readPosition, scrollToClosed])\n\n useEffect(\n () => () => {\n if (debounceTimer.current) {\n clearTimeout(debounceTimer.current)\n }\n },\n [],\n )\n\n const handleScroll = useCallback(\n (e: UIEvent<HTMLDivElement>) => {\n onScrollProp?.(e)\n const el = containerRef.current\n if (!el) return\n const m = getMaxScroll(el)\n const pos = getRevealFromScroll(el.scrollLeft, m, wL, wR, mode)\n const closed = getScrollClosed(wL, wR, m, mode)\n if (Math.abs(el.scrollLeft - closed) > restEpsilon) {\n swipedRef.current = true\n }\n if (debounceTimer.current) {\n clearTimeout(debounceTimer.current)\n }\n debounceTimer.current = setTimeout(() => {\n debounceTimer.current = null\n emitReveal(pos)\n }, SCROLL_REVEAL_DEBOUNCE_MS)\n },\n [emitReveal, mode, onScrollProp, wL, wR],\n )\n\n const onClickCapture = useCallback((e: MouseEvent) => {\n const t = e.target as Node | null\n if (t) {\n if (leftRef.current?.contains(t) || rightRef.current?.contains(t)) {\n return\n }\n }\n if (swipedRef.current) {\n e.preventDefault()\n e.stopPropagation()\n swipedRef.current = false\n }\n }, [])\n\n // `100%` for the main track (not `1fr`) so total row width > viewport: otherwise both\n // columns can shrink to fit with no overflow and the action stays visible.\n const gridTemplateColumns =\n mode === REVEAL_MODE.right\n ? `100% ${wR}px`\n : mode === REVEAL_MODE.left\n ? `${wL}px 100%`\n : `${wL}px 100% ${wR}px`\n\n const handleContent = showHandle ? (\n handleOverride !== undefined ? (\n handleOverride\n ) : (\n <>\n <span\n className=\"sr-only\"\n style={{\n position: 'absolute',\n width: 1,\n height: 1,\n padding: 0,\n margin: -1,\n overflow: 'hidden',\n clip: 'rect(0,0,0,0)',\n border: 0,\n }}\n >\n {handleAriaLabel}\n </span>\n <span className={classNames.handleIcon} aria-hidden>\n <DefaultHandleIcon />\n </span>\n </>\n )\n ) : null\n\n const handleStrip = showHandle ? (\n <div\n role=\"presentation\"\n className={classNames.handleContainer}\n data-reveal-row-handle\n style={{ flexShrink: 0, alignSelf: 'stretch', display: 'flex', alignItems: 'center' }}\n title={handleOverride === undefined ? handleTitle : undefined}\n >\n {handleContent}\n </div>\n ) : null\n\n const mainColumn = (\n <div\n className={classNames.main}\n data-reveal-row-main\n style={{\n ...snapStart,\n minWidth: 0,\n width: '100%',\n maxWidth: '100%',\n boxSizing: 'border-box',\n }}\n >\n <div\n className={classNames.mainInner}\n style={{\n display: 'flex',\n minWidth: 0,\n width: '100%',\n alignItems: 'flex-start',\n }}\n >\n {handlePosition === REVEAL_HANDLE_POSITION.start && handleStrip}\n <div style={{ minWidth: 0, flex: 1, flexBasis: 0 }}>{children}</div>\n {handlePosition === REVEAL_HANDLE_POSITION.end && handleStrip}\n </div>\n </div>\n )\n\n return (\n <div\n ref={containerRef}\n data-reveal-mode={mode}\n className={cx(classNames.root, className)}\n onScroll={handleScroll}\n onClickCapture={onClickCapture}\n style={{\n ...rootScrollStyle,\n ...style,\n gridTemplateColumns,\n }}\n >\n {hasL ? (\n <div\n ref={leftRef}\n className={classNames.left}\n data-reveal-row-left\n style={{ ...snapStart, minWidth: 0, width: wL }}\n >\n {left}\n </div>\n ) : null}\n {mainColumn}\n {hasR ? (\n <div\n ref={rightRef}\n className={classNames.right}\n data-reveal-row-right\n style={{ ...snapEnd, minWidth: 0, width: wR }}\n >\n {right}\n </div>\n ) : null}\n </div>\n )\n}\n\nexport const RevealRow = forwardRef<RevealRowHandle, RevealRowProps>(\n function RevealRow(props, ref) {\n return <RevealRowInner {...props} forwardedRef={ref} />\n },\n)\n","/** Minimal 6-dot grip, no icon library dependency. */\nexport function DefaultHandleIcon() {\n return (\n <svg\n width={16}\n height={16}\n viewBox=\"0 0 16 16\"\n fill=\"currentColor\"\n aria-hidden\n >\n <circle cx=\"6\" cy=\"4\" r=\"1.2\" />\n <circle cx=\"10\" cy=\"4\" r=\"1.2\" />\n <circle cx=\"6\" cy=\"8\" r=\"1.2\" />\n <circle cx=\"10\" cy=\"8\" r=\"1.2\" />\n <circle cx=\"6\" cy=\"12\" r=\"1.2\" />\n <circle cx=\"10\" cy=\"12\" r=\"1.2\" />\n </svg>\n )\n}\n"],"mappings":"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","names":["import_jsx_runtime","RevealRow"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,365 @@
1
+ // src/types.ts
2
+ var REVEAL_MODE = {
3
+ left: "left",
4
+ right: "right",
5
+ both: "both"
6
+ };
7
+ var REVEAL_POSITION = {
8
+ left: "left",
9
+ center: "center",
10
+ right: "right"
11
+ };
12
+ var REVEAL_HANDLE_POSITION = {
13
+ start: "start",
14
+ end: "end"
15
+ };
16
+
17
+ // src/getRevealFromScroll.ts
18
+ function getRevealFromScroll(scrollLeft, maxScroll, wL, wR, mode) {
19
+ if (mode === REVEAL_MODE.right) {
20
+ if (maxScroll <= 0) return REVEAL_POSITION.center;
21
+ return scrollLeft < wR / 2 ? REVEAL_POSITION.center : REVEAL_POSITION.right;
22
+ }
23
+ if (mode === REVEAL_MODE.left) {
24
+ if (maxScroll <= 0) return REVEAL_POSITION.center;
25
+ return scrollLeft < wL / 2 ? REVEAL_POSITION.left : REVEAL_POSITION.center;
26
+ }
27
+ if (maxScroll <= 0) return REVEAL_POSITION.center;
28
+ if (scrollLeft < wL / 2) return REVEAL_POSITION.left;
29
+ if (scrollLeft > maxScroll - wR / 2) return REVEAL_POSITION.right;
30
+ return REVEAL_POSITION.center;
31
+ }
32
+ function getScrollClosed(wL, _wR, _maxScroll, mode) {
33
+ if (mode === REVEAL_MODE.right) return 0;
34
+ return wL;
35
+ }
36
+
37
+ // src/RevealRow.tsx
38
+ import {
39
+ forwardRef,
40
+ useCallback,
41
+ useEffect,
42
+ useImperativeHandle,
43
+ useLayoutEffect,
44
+ useRef
45
+ } from "react";
46
+
47
+ // src/DefaultHandleIcon.tsx
48
+ import { jsx, jsxs } from "react/jsx-runtime";
49
+ function DefaultHandleIcon() {
50
+ return /* @__PURE__ */ jsxs(
51
+ "svg",
52
+ {
53
+ width: 16,
54
+ height: 16,
55
+ viewBox: "0 0 16 16",
56
+ fill: "currentColor",
57
+ "aria-hidden": true,
58
+ children: [
59
+ /* @__PURE__ */ jsx("circle", { cx: "6", cy: "4", r: "1.2" }),
60
+ /* @__PURE__ */ jsx("circle", { cx: "10", cy: "4", r: "1.2" }),
61
+ /* @__PURE__ */ jsx("circle", { cx: "6", cy: "8", r: "1.2" }),
62
+ /* @__PURE__ */ jsx("circle", { cx: "10", cy: "8", r: "1.2" }),
63
+ /* @__PURE__ */ jsx("circle", { cx: "6", cy: "12", r: "1.2" }),
64
+ /* @__PURE__ */ jsx("circle", { cx: "10", cy: "12", r: "1.2" })
65
+ ]
66
+ }
67
+ );
68
+ }
69
+
70
+ // src/RevealRow.tsx
71
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
72
+ function cx(...parts) {
73
+ return parts.filter(Boolean).join(" ");
74
+ }
75
+ function getMaxScroll(el) {
76
+ return Math.max(0, el.scrollWidth - el.clientWidth);
77
+ }
78
+ function resolveMode(left, right, mode) {
79
+ if (mode) return mode;
80
+ if (left != null && right != null) return REVEAL_MODE.both;
81
+ if (left != null) return REVEAL_MODE.left;
82
+ return REVEAL_MODE.right;
83
+ }
84
+ var SCROLL_REVEAL_DEBOUNCE_MS = 64;
85
+ var restEpsilon = 2;
86
+ var rootScrollStyle = {
87
+ display: "grid",
88
+ overflowX: "auto",
89
+ scrollSnapType: "x mandatory",
90
+ scrollbarWidth: "none",
91
+ WebkitOverflowScrolling: "touch",
92
+ overscrollBehaviorX: "contain",
93
+ touchAction: "pan-x pan-y"
94
+ };
95
+ var snapStart = { scrollSnapAlign: "start" };
96
+ var snapEnd = { scrollSnapAlign: "end" };
97
+ function RevealRowInner({
98
+ children,
99
+ left,
100
+ right,
101
+ mode: modeProp,
102
+ actionWidthLeft: wLIn,
103
+ actionWidthRight: wRIn,
104
+ classNames = {},
105
+ showHandle = true,
106
+ handle: handleOverride,
107
+ handlePosition: handlePositionProp,
108
+ handleTitle = "Drag horizontally to show actions",
109
+ handleAriaLabel = "Drag horizontally to show actions",
110
+ onRevealChange,
111
+ onScroll: onScrollProp,
112
+ resetWhenDisabled = true,
113
+ disabled = false,
114
+ isActive = false,
115
+ className,
116
+ style,
117
+ forwardedRef
118
+ }) {
119
+ const mode = resolveMode(left, right, modeProp);
120
+ const hasL = mode === REVEAL_MODE.left || mode === REVEAL_MODE.both;
121
+ const hasR = mode === REVEAL_MODE.right || mode === REVEAL_MODE.both;
122
+ const wL = hasL ? wLIn ?? 88 : 0;
123
+ const wR = hasR ? wRIn ?? 88 : 0;
124
+ const handlePosition = handlePositionProp ?? (mode === REVEAL_MODE.left ? REVEAL_HANDLE_POSITION.start : REVEAL_HANDLE_POSITION.end);
125
+ const containerRef = useRef(null);
126
+ const leftRef = useRef(null);
127
+ const rightRef = useRef(null);
128
+ const lastEmitted = useRef(null);
129
+ const swipedRef = useRef(false);
130
+ const debounceTimer = useRef(null);
131
+ const hasAppliedInitialScroll = useRef(false);
132
+ const readPosition = useCallback(() => {
133
+ const el = containerRef.current;
134
+ if (!el) return REVEAL_POSITION.center;
135
+ return getRevealFromScroll(el.scrollLeft, getMaxScroll(el), wL, wR, mode);
136
+ }, [wL, wR, mode]);
137
+ const emitReveal = useCallback(
138
+ (position) => {
139
+ if (lastEmitted.current === position) return;
140
+ lastEmitted.current = position;
141
+ onRevealChange?.(position);
142
+ },
143
+ [onRevealChange]
144
+ );
145
+ const scrollToClosed = useCallback(() => {
146
+ const el = containerRef.current;
147
+ if (!el) return;
148
+ const m = getMaxScroll(el);
149
+ const closed = getScrollClosed(wL, wR, m, mode);
150
+ el.scrollLeft = closed;
151
+ }, [wL, wR, mode]);
152
+ useImperativeHandle(
153
+ forwardedRef,
154
+ () => ({
155
+ close: () => {
156
+ scrollToClosed();
157
+ const p = readPosition();
158
+ if (lastEmitted.current !== p) {
159
+ lastEmitted.current = p;
160
+ onRevealChange?.(p);
161
+ }
162
+ },
163
+ reveal: (position) => {
164
+ const el = containerRef.current;
165
+ if (!el) return;
166
+ const max = getMaxScroll(el);
167
+ if (position === REVEAL_POSITION.center) {
168
+ el.scrollLeft = getScrollClosed(wL, wR, max, mode);
169
+ } else if (position === REVEAL_POSITION.left && (mode === REVEAL_MODE.left || mode === REVEAL_MODE.both)) {
170
+ el.scrollLeft = 0;
171
+ } else if (position === REVEAL_POSITION.right && (mode === REVEAL_MODE.right || mode === REVEAL_MODE.both)) {
172
+ el.scrollLeft = max;
173
+ } else {
174
+ return;
175
+ }
176
+ const p = getRevealFromScroll(el.scrollLeft, max, wL, wR, mode);
177
+ lastEmitted.current = p;
178
+ onRevealChange?.(p);
179
+ }
180
+ }),
181
+ [mode, onRevealChange, readPosition, scrollToClosed, wL, wR]
182
+ );
183
+ useLayoutEffect(() => {
184
+ if (mode === REVEAL_MODE.right) {
185
+ return;
186
+ }
187
+ const el = containerRef.current;
188
+ if (!el) return;
189
+ if (!hasAppliedInitialScroll.current) {
190
+ el.scrollLeft = wL;
191
+ hasAppliedInitialScroll.current = true;
192
+ }
193
+ }, [mode, wL]);
194
+ useEffect(() => {
195
+ if (disabled && resetWhenDisabled) {
196
+ hasAppliedInitialScroll.current = true;
197
+ scrollToClosed();
198
+ emitReveal(readPosition());
199
+ }
200
+ }, [disabled, emitReveal, readPosition, resetWhenDisabled, scrollToClosed]);
201
+ useEffect(() => {
202
+ if (isActive) {
203
+ hasAppliedInitialScroll.current = true;
204
+ scrollToClosed();
205
+ emitReveal(readPosition());
206
+ }
207
+ }, [emitReveal, isActive, readPosition, scrollToClosed]);
208
+ useEffect(
209
+ () => () => {
210
+ if (debounceTimer.current) {
211
+ clearTimeout(debounceTimer.current);
212
+ }
213
+ },
214
+ []
215
+ );
216
+ const handleScroll = useCallback(
217
+ (e) => {
218
+ onScrollProp?.(e);
219
+ const el = containerRef.current;
220
+ if (!el) return;
221
+ const m = getMaxScroll(el);
222
+ const pos = getRevealFromScroll(el.scrollLeft, m, wL, wR, mode);
223
+ const closed = getScrollClosed(wL, wR, m, mode);
224
+ if (Math.abs(el.scrollLeft - closed) > restEpsilon) {
225
+ swipedRef.current = true;
226
+ }
227
+ if (debounceTimer.current) {
228
+ clearTimeout(debounceTimer.current);
229
+ }
230
+ debounceTimer.current = setTimeout(() => {
231
+ debounceTimer.current = null;
232
+ emitReveal(pos);
233
+ }, SCROLL_REVEAL_DEBOUNCE_MS);
234
+ },
235
+ [emitReveal, mode, onScrollProp, wL, wR]
236
+ );
237
+ const onClickCapture = useCallback((e) => {
238
+ const t = e.target;
239
+ if (t) {
240
+ if (leftRef.current?.contains(t) || rightRef.current?.contains(t)) {
241
+ return;
242
+ }
243
+ }
244
+ if (swipedRef.current) {
245
+ e.preventDefault();
246
+ e.stopPropagation();
247
+ swipedRef.current = false;
248
+ }
249
+ }, []);
250
+ const gridTemplateColumns = mode === REVEAL_MODE.right ? `100% ${wR}px` : mode === REVEAL_MODE.left ? `${wL}px 100%` : `${wL}px 100% ${wR}px`;
251
+ const handleContent = showHandle ? handleOverride !== void 0 ? handleOverride : /* @__PURE__ */ jsxs2(Fragment, { children: [
252
+ /* @__PURE__ */ jsx2(
253
+ "span",
254
+ {
255
+ className: "sr-only",
256
+ style: {
257
+ position: "absolute",
258
+ width: 1,
259
+ height: 1,
260
+ padding: 0,
261
+ margin: -1,
262
+ overflow: "hidden",
263
+ clip: "rect(0,0,0,0)",
264
+ border: 0
265
+ },
266
+ children: handleAriaLabel
267
+ }
268
+ ),
269
+ /* @__PURE__ */ jsx2("span", { className: classNames.handleIcon, "aria-hidden": true, children: /* @__PURE__ */ jsx2(DefaultHandleIcon, {}) })
270
+ ] }) : null;
271
+ const handleStrip = showHandle ? /* @__PURE__ */ jsx2(
272
+ "div",
273
+ {
274
+ role: "presentation",
275
+ className: classNames.handleContainer,
276
+ "data-reveal-row-handle": true,
277
+ style: { flexShrink: 0, alignSelf: "stretch", display: "flex", alignItems: "center" },
278
+ title: handleOverride === void 0 ? handleTitle : void 0,
279
+ children: handleContent
280
+ }
281
+ ) : null;
282
+ const mainColumn = /* @__PURE__ */ jsx2(
283
+ "div",
284
+ {
285
+ className: classNames.main,
286
+ "data-reveal-row-main": true,
287
+ style: {
288
+ ...snapStart,
289
+ minWidth: 0,
290
+ width: "100%",
291
+ maxWidth: "100%",
292
+ boxSizing: "border-box"
293
+ },
294
+ children: /* @__PURE__ */ jsxs2(
295
+ "div",
296
+ {
297
+ className: classNames.mainInner,
298
+ style: {
299
+ display: "flex",
300
+ minWidth: 0,
301
+ width: "100%",
302
+ alignItems: "flex-start"
303
+ },
304
+ children: [
305
+ handlePosition === REVEAL_HANDLE_POSITION.start && handleStrip,
306
+ /* @__PURE__ */ jsx2("div", { style: { minWidth: 0, flex: 1, flexBasis: 0 }, children }),
307
+ handlePosition === REVEAL_HANDLE_POSITION.end && handleStrip
308
+ ]
309
+ }
310
+ )
311
+ }
312
+ );
313
+ return /* @__PURE__ */ jsxs2(
314
+ "div",
315
+ {
316
+ ref: containerRef,
317
+ "data-reveal-mode": mode,
318
+ className: cx(classNames.root, className),
319
+ onScroll: handleScroll,
320
+ onClickCapture,
321
+ style: {
322
+ ...rootScrollStyle,
323
+ ...style,
324
+ gridTemplateColumns
325
+ },
326
+ children: [
327
+ hasL ? /* @__PURE__ */ jsx2(
328
+ "div",
329
+ {
330
+ ref: leftRef,
331
+ className: classNames.left,
332
+ "data-reveal-row-left": true,
333
+ style: { ...snapStart, minWidth: 0, width: wL },
334
+ children: left
335
+ }
336
+ ) : null,
337
+ mainColumn,
338
+ hasR ? /* @__PURE__ */ jsx2(
339
+ "div",
340
+ {
341
+ ref: rightRef,
342
+ className: classNames.right,
343
+ "data-reveal-row-right": true,
344
+ style: { ...snapEnd, minWidth: 0, width: wR },
345
+ children: right
346
+ }
347
+ ) : null
348
+ ]
349
+ }
350
+ );
351
+ }
352
+ var RevealRow = forwardRef(
353
+ function RevealRow2(props, ref) {
354
+ return /* @__PURE__ */ jsx2(RevealRowInner, { ...props, forwardedRef: ref });
355
+ }
356
+ );
357
+ export {
358
+ REVEAL_HANDLE_POSITION,
359
+ REVEAL_MODE,
360
+ REVEAL_POSITION,
361
+ RevealRow,
362
+ getRevealFromScroll,
363
+ getScrollClosed
364
+ };
365
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts","../src/getRevealFromScroll.ts","../src/RevealRow.tsx","../src/DefaultHandleIcon.tsx"],"sourcesContent":["import type { CSSProperties, ReactNode, UIEvent } from 'react'\n\nexport const REVEAL_MODE = {\n left: 'left',\n right: 'right',\n both: 'both',\n} as const\n\nexport type RevealMode = (typeof REVEAL_MODE)[keyof typeof REVEAL_MODE]\n\nexport const REVEAL_POSITION = {\n left: 'left',\n center: 'center',\n right: 'right',\n} as const\n\nexport type RevealPosition =\n (typeof REVEAL_POSITION)[keyof typeof REVEAL_POSITION]\n\nexport const REVEAL_HANDLE_POSITION = {\n start: 'start',\n end: 'end',\n} as const\n\nexport type RevealHandlePosition =\n (typeof REVEAL_HANDLE_POSITION)[keyof typeof REVEAL_HANDLE_POSITION]\n\nexport type RevealRowClassNames = {\n root?: string\n main?: string\n mainInner?: string\n left?: string\n right?: string\n handleContainer?: string\n handleIcon?: string\n}\n\nexport type RevealRowHandle = {\n close: () => void\n reveal: (position: RevealPosition) => void\n}\n\nexport type RevealRowProps = {\n /** Primary row content (e.g. title, subtitle). */\n children: ReactNode\n /** Optional leading action column (e.g. delete). Omitted in `right` mode. */\n left?: ReactNode\n /**\n * Trailing action column (e.g. add). Omitted in `left` mode.\n */\n right?: ReactNode\n /**\n * If both `left` and `right` are set, defaults to `both`. If only one side,\n * mode is derived unless overridden.\n */\n mode?: RevealMode\n actionWidthLeft?: number\n actionWidthRight?: number\n classNames?: RevealRowClassNames\n /** @default true */\n showHandle?: boolean\n /** Replaces the built-in drag affordance. */\n handle?: ReactNode\n /**\n * Where the handle sits in the main row. Default: `start` in `left` mode,\n * `end` otherwise.\n */\n handlePosition?: RevealHandlePosition\n handleTitle?: string\n /** Shown in screen-reader text when using the default drag handle. */\n handleAriaLabel?: string\n /** Fires when the settled position changes. */\n onRevealChange?: (position: RevealPosition) => void\n onScroll?: (event: UIEvent<HTMLDivElement>) => void\n /** Resets to closed; default true. */\n resetWhenDisabled?: boolean\n disabled?: boolean\n /**\n * When true, snaps back to the closed position (e.g. row became active/selected\n * elsewhere in the list).\n */\n isActive?: boolean\n className?: string\n style?: CSSProperties\n}\n","import {\n REVEAL_MODE,\n REVEAL_POSITION,\n type RevealMode,\n type RevealPosition,\n} from './types'\n\n/**\n * Map horizontal scroll position to a logical \"revealed\" state.\n * `wL` / `wR` are the fixed action column widths; `maxScroll` is\n * `Math.max(0, scrollWidth - clientWidth)`.\n */\nexport function getRevealFromScroll(\n scrollLeft: number,\n maxScroll: number,\n wL: number,\n wR: number,\n mode: RevealMode,\n): RevealPosition {\n if (mode === REVEAL_MODE.right) {\n if (maxScroll <= 0) return REVEAL_POSITION.center\n return scrollLeft < wR / 2 ? REVEAL_POSITION.center : REVEAL_POSITION.right\n }\n if (mode === REVEAL_MODE.left) {\n if (maxScroll <= 0) return REVEAL_POSITION.center\n return scrollLeft < wL / 2 ? REVEAL_POSITION.left : REVEAL_POSITION.center\n }\n if (maxScroll <= 0) return REVEAL_POSITION.center\n if (scrollLeft < wL / 2) return REVEAL_POSITION.left\n if (scrollLeft > maxScroll - wR / 2) return REVEAL_POSITION.right\n return REVEAL_POSITION.center\n}\n\n/** \"Closed\" / resting scrollLeft for a mode (main panel only). */\nexport function getScrollClosed(\n wL: number,\n _wR: number,\n _maxScroll: number,\n mode: RevealMode,\n): number {\n if (mode === REVEAL_MODE.right) return 0\n return wL\n}\n","'use client'\n\nimport type { CSSProperties, ForwardedRef, MouseEvent, UIEvent } from 'react'\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useLayoutEffect,\n useRef,\n} from 'react'\n\nimport { DefaultHandleIcon } from './DefaultHandleIcon'\nimport { getRevealFromScroll, getScrollClosed } from './getRevealFromScroll'\nimport {\n REVEAL_HANDLE_POSITION,\n REVEAL_MODE,\n REVEAL_POSITION,\n type RevealHandlePosition,\n type RevealMode,\n type RevealPosition,\n type RevealRowClassNames,\n type RevealRowHandle,\n type RevealRowProps,\n} from './types'\n\nfunction cx(...parts: (string | undefined)[]) {\n return parts.filter(Boolean).join(' ')\n}\n\nfunction getMaxScroll(el: HTMLDivElement) {\n return Math.max(0, el.scrollWidth - el.clientWidth)\n}\n\nfunction resolveMode(\n left: RevealRowProps['left'],\n right: RevealRowProps['right'],\n mode: RevealMode | undefined,\n): RevealMode {\n if (mode) return mode\n if (left != null && right != null) return REVEAL_MODE.both\n if (left != null) return REVEAL_MODE.left\n return REVEAL_MODE.right\n}\n\nconst SCROLL_REVEAL_DEBOUNCE_MS = 64\nconst restEpsilon = 2\n\nconst rootScrollStyle: CSSProperties = {\n display: 'grid',\n overflowX: 'auto',\n scrollSnapType: 'x mandatory',\n scrollbarWidth: 'none',\n WebkitOverflowScrolling: 'touch',\n overscrollBehaviorX: 'contain',\n touchAction: 'pan-x pan-y',\n}\n\nconst snapStart: CSSProperties = { scrollSnapAlign: 'start' }\nconst snapEnd: CSSProperties = { scrollSnapAlign: 'end' }\n\ntype InnerProps = RevealRowProps & {\n forwardedRef: ForwardedRef<RevealRowHandle>\n}\n\nfunction RevealRowInner({\n children,\n left,\n right,\n mode: modeProp,\n actionWidthLeft: wLIn,\n actionWidthRight: wRIn,\n classNames = {} as RevealRowClassNames,\n showHandle = true,\n handle: handleOverride,\n handlePosition: handlePositionProp,\n handleTitle = 'Drag horizontally to show actions',\n handleAriaLabel = 'Drag horizontally to show actions',\n onRevealChange,\n onScroll: onScrollProp,\n resetWhenDisabled = true,\n disabled = false,\n isActive = false,\n className,\n style,\n forwardedRef,\n}: InnerProps) {\n const mode = resolveMode(left, right, modeProp)\n const hasL = mode === REVEAL_MODE.left || mode === REVEAL_MODE.both\n const hasR = mode === REVEAL_MODE.right || mode === REVEAL_MODE.both\n const wL = hasL ? (wLIn ?? 88) : 0\n const wR = hasR ? (wRIn ?? 88) : 0\n\n const handlePosition: RevealHandlePosition =\n handlePositionProp ??\n (mode === REVEAL_MODE.left\n ? REVEAL_HANDLE_POSITION.start\n : REVEAL_HANDLE_POSITION.end)\n\n const containerRef = useRef<HTMLDivElement>(null)\n const leftRef = useRef<HTMLDivElement>(null)\n const rightRef = useRef<HTMLDivElement>(null)\n const lastEmitted = useRef<RevealPosition | null>(null)\n const swipedRef = useRef(false)\n const debounceTimer = useRef<ReturnType<typeof setTimeout> | null>(null)\n const hasAppliedInitialScroll = useRef(false)\n\n const readPosition = useCallback((): RevealPosition => {\n const el = containerRef.current\n if (!el) return REVEAL_POSITION.center\n return getRevealFromScroll(el.scrollLeft, getMaxScroll(el), wL, wR, mode)\n }, [wL, wR, mode])\n\n const emitReveal = useCallback(\n (position: RevealPosition) => {\n if (lastEmitted.current === position) return\n lastEmitted.current = position\n onRevealChange?.(position)\n },\n [onRevealChange],\n )\n\n const scrollToClosed = useCallback(() => {\n const el = containerRef.current\n if (!el) return\n const m = getMaxScroll(el)\n const closed = getScrollClosed(wL, wR, m, mode)\n el.scrollLeft = closed\n }, [wL, wR, mode])\n\n useImperativeHandle(\n forwardedRef,\n () => ({\n close: () => {\n scrollToClosed()\n const p = readPosition()\n if (lastEmitted.current !== p) {\n lastEmitted.current = p\n onRevealChange?.(p)\n }\n },\n reveal: (position: RevealPosition) => {\n const el = containerRef.current\n if (!el) return\n const max = getMaxScroll(el)\n if (position === REVEAL_POSITION.center) {\n el.scrollLeft = getScrollClosed(wL, wR, max, mode)\n } else if (\n position === REVEAL_POSITION.left &&\n (mode === REVEAL_MODE.left || mode === REVEAL_MODE.both)\n ) {\n el.scrollLeft = 0\n } else if (\n position === REVEAL_POSITION.right &&\n (mode === REVEAL_MODE.right || mode === REVEAL_MODE.both)\n ) {\n el.scrollLeft = max\n } else {\n return\n }\n const p = getRevealFromScroll(el.scrollLeft, max, wL, wR, mode)\n lastEmitted.current = p\n onRevealChange?.(p)\n },\n }),\n [mode, onRevealChange, readPosition, scrollToClosed, wL, wR],\n )\n\n useLayoutEffect(() => {\n if (mode === REVEAL_MODE.right) {\n return\n }\n const el = containerRef.current\n if (!el) return\n if (!hasAppliedInitialScroll.current) {\n el.scrollLeft = wL\n hasAppliedInitialScroll.current = true\n }\n }, [mode, wL])\n\n useEffect(() => {\n if (disabled && resetWhenDisabled) {\n hasAppliedInitialScroll.current = true\n scrollToClosed()\n emitReveal(readPosition())\n }\n }, [disabled, emitReveal, readPosition, resetWhenDisabled, scrollToClosed])\n\n useEffect(() => {\n if (isActive) {\n hasAppliedInitialScroll.current = true\n scrollToClosed()\n emitReveal(readPosition())\n }\n }, [emitReveal, isActive, readPosition, scrollToClosed])\n\n useEffect(\n () => () => {\n if (debounceTimer.current) {\n clearTimeout(debounceTimer.current)\n }\n },\n [],\n )\n\n const handleScroll = useCallback(\n (e: UIEvent<HTMLDivElement>) => {\n onScrollProp?.(e)\n const el = containerRef.current\n if (!el) return\n const m = getMaxScroll(el)\n const pos = getRevealFromScroll(el.scrollLeft, m, wL, wR, mode)\n const closed = getScrollClosed(wL, wR, m, mode)\n if (Math.abs(el.scrollLeft - closed) > restEpsilon) {\n swipedRef.current = true\n }\n if (debounceTimer.current) {\n clearTimeout(debounceTimer.current)\n }\n debounceTimer.current = setTimeout(() => {\n debounceTimer.current = null\n emitReveal(pos)\n }, SCROLL_REVEAL_DEBOUNCE_MS)\n },\n [emitReveal, mode, onScrollProp, wL, wR],\n )\n\n const onClickCapture = useCallback((e: MouseEvent) => {\n const t = e.target as Node | null\n if (t) {\n if (leftRef.current?.contains(t) || rightRef.current?.contains(t)) {\n return\n }\n }\n if (swipedRef.current) {\n e.preventDefault()\n e.stopPropagation()\n swipedRef.current = false\n }\n }, [])\n\n // `100%` for the main track (not `1fr`) so total row width > viewport: otherwise both\n // columns can shrink to fit with no overflow and the action stays visible.\n const gridTemplateColumns =\n mode === REVEAL_MODE.right\n ? `100% ${wR}px`\n : mode === REVEAL_MODE.left\n ? `${wL}px 100%`\n : `${wL}px 100% ${wR}px`\n\n const handleContent = showHandle ? (\n handleOverride !== undefined ? (\n handleOverride\n ) : (\n <>\n <span\n className=\"sr-only\"\n style={{\n position: 'absolute',\n width: 1,\n height: 1,\n padding: 0,\n margin: -1,\n overflow: 'hidden',\n clip: 'rect(0,0,0,0)',\n border: 0,\n }}\n >\n {handleAriaLabel}\n </span>\n <span className={classNames.handleIcon} aria-hidden>\n <DefaultHandleIcon />\n </span>\n </>\n )\n ) : null\n\n const handleStrip = showHandle ? (\n <div\n role=\"presentation\"\n className={classNames.handleContainer}\n data-reveal-row-handle\n style={{ flexShrink: 0, alignSelf: 'stretch', display: 'flex', alignItems: 'center' }}\n title={handleOverride === undefined ? handleTitle : undefined}\n >\n {handleContent}\n </div>\n ) : null\n\n const mainColumn = (\n <div\n className={classNames.main}\n data-reveal-row-main\n style={{\n ...snapStart,\n minWidth: 0,\n width: '100%',\n maxWidth: '100%',\n boxSizing: 'border-box',\n }}\n >\n <div\n className={classNames.mainInner}\n style={{\n display: 'flex',\n minWidth: 0,\n width: '100%',\n alignItems: 'flex-start',\n }}\n >\n {handlePosition === REVEAL_HANDLE_POSITION.start && handleStrip}\n <div style={{ minWidth: 0, flex: 1, flexBasis: 0 }}>{children}</div>\n {handlePosition === REVEAL_HANDLE_POSITION.end && handleStrip}\n </div>\n </div>\n )\n\n return (\n <div\n ref={containerRef}\n data-reveal-mode={mode}\n className={cx(classNames.root, className)}\n onScroll={handleScroll}\n onClickCapture={onClickCapture}\n style={{\n ...rootScrollStyle,\n ...style,\n gridTemplateColumns,\n }}\n >\n {hasL ? (\n <div\n ref={leftRef}\n className={classNames.left}\n data-reveal-row-left\n style={{ ...snapStart, minWidth: 0, width: wL }}\n >\n {left}\n </div>\n ) : null}\n {mainColumn}\n {hasR ? (\n <div\n ref={rightRef}\n className={classNames.right}\n data-reveal-row-right\n style={{ ...snapEnd, minWidth: 0, width: wR }}\n >\n {right}\n </div>\n ) : null}\n </div>\n )\n}\n\nexport const RevealRow = forwardRef<RevealRowHandle, RevealRowProps>(\n function RevealRow(props, ref) {\n return <RevealRowInner {...props} forwardedRef={ref} />\n },\n)\n","/** Minimal 6-dot grip, no icon library dependency. */\nexport function DefaultHandleIcon() {\n return (\n <svg\n width={16}\n height={16}\n viewBox=\"0 0 16 16\"\n fill=\"currentColor\"\n aria-hidden\n >\n <circle cx=\"6\" cy=\"4\" r=\"1.2\" />\n <circle cx=\"10\" cy=\"4\" r=\"1.2\" />\n <circle cx=\"6\" cy=\"8\" r=\"1.2\" />\n <circle cx=\"10\" cy=\"8\" r=\"1.2\" />\n <circle cx=\"6\" cy=\"12\" r=\"1.2\" />\n <circle cx=\"10\" cy=\"12\" r=\"1.2\" />\n </svg>\n )\n}\n"],"mappings":"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","names":["jsx","jsxs","RevealRow"]}
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "@present-day/reveal-row",
3
+ "version": "0.1.0",
4
+ "description": "Horizontal scroll-to-reveal row for actions on the left, right, or both sides",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "sideEffects": false,
19
+ "packageManager": "bun@1.1.42",
20
+ "scripts": {
21
+ "playground": "vite --config playground/vite.config.ts",
22
+ "playground:build": "vite build --config playground/vite.config.ts",
23
+ "playground:preview": "vite preview --config playground/vite.config.ts",
24
+ "build": "tsup",
25
+ "dev": "tsup --watch",
26
+ "lint": "biome check .",
27
+ "format": "biome check --write .",
28
+ "type-check": "tsc --noEmit",
29
+ "test": "bun --bun vitest run",
30
+ "test:coverage": "bun --bun vitest run --coverage",
31
+ "test:watch": "bun --bun vitest"
32
+ },
33
+ "keywords": [
34
+ "react",
35
+ "reveal",
36
+ "swipe",
37
+ "scroll",
38
+ "list-row",
39
+ "actions",
40
+ "mobile"
41
+ ],
42
+ "author": "Present Day",
43
+ "license": "MIT",
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "git+https://github.com/present-day/reveal-row.git"
47
+ },
48
+ "bugs": {
49
+ "url": "https://github.com/present-day/reveal-row/issues"
50
+ },
51
+ "homepage": "https://github.com/present-day/reveal-row#readme",
52
+ "publishConfig": {
53
+ "access": "public"
54
+ },
55
+ "peerDependencies": {
56
+ "react": ">=18.0.0",
57
+ "react-dom": ">=18.0.0"
58
+ },
59
+ "devDependencies": {
60
+ "@biomejs/biome": "^2.4.13",
61
+ "@types/react": "^19.2.14",
62
+ "@types/react-dom": "^19.2.3",
63
+ "@vitejs/plugin-react": "^6.0.1",
64
+ "@vitest/coverage-istanbul": "^4.1.5",
65
+ "react": "^19.2.5",
66
+ "react-dom": "^19.2.5",
67
+ "tsup": "^8.5.1",
68
+ "typescript": "^6.0.3",
69
+ "vite": "^8.0.10",
70
+ "vitest": "^4.1.5"
71
+ }
72
+ }