@swan-io/lake 8.18.13 → 9.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swan-io/lake",
3
- "version": "8.18.13",
3
+ "version": "9.0.0",
4
4
  "engines": {
5
5
  "node": ">=20.9.0",
6
6
  "yarn": "^1.22.0"
@@ -1,109 +0,0 @@
1
- /**
2
- * ## FixedListView
3
- *
4
- * The FixedListView is a component designed to render big amounts of tabular data.
5
- *
6
- * For usability, the data can be display in three types of columns:
7
- *
8
- * - Sticked to start columns (usually the main identifier, always visible)
9
- * - Center columns (scrollable columns, with additional information)
10
- * - Sticked to end columns (so that some actions are always accesible at the end of each line)
11
- *
12
- * ┌────────────────────────────────────────────────────────────────────────────────────────┐
13
- * │ ┌────────────────┐ ┌────────────────────────────────────────┐ ┌────────────────┐ │
14
- * │ │ ╔════════════╗ │ │ ╔════════════════════════════════════╗ │ │ ╔════════════╗ │ │
15
- * │ │ ║ Header ║ │ │ ║ Header ║ │ │ ║ Header ║ │ ▲ │
16
- * │ │ ║ ║ │ │ ║◀──────────────────────────────────▶║ │ │ ║ ║ │ │ │
17
- * │ │ ╚════════════╝ │ │ ╚════════════════════════════════════╝ │ │ ╚════════════╝ │ │ │
18
- * │ │ │ │ │ │ │ │ │
19
- * │ │ ┌───────────┐ │ │ ┌────────────────────────────────────┐ │ │ ┌────────────┐ │ │ │
20
- * │ │ │ Cell A1 │──┼─┼▷│ Cell B1 ├─┼─┼─▷ Cell C1 │ │ │ │
21
- * │ │ └───────────┘ │ │ └────────────────────────────────────┘ │ │ └────────────┘ │ │ │
22
- * │ │ ┌───────────┐ │ │ ┌────────────────────────────────────┐ │ │ ┌────────────┐ │ │ │
23
- * │ │ │ Cell A2 │──┼─┼▷│ Cell B2 │─┼─┼─▷ Cell C2 │ │ │ │
24
- * │ │ └───────────┘ │ │ └────────────────────────────────────┘ │ │ └────────────┘ │ │ │
25
- * │ │ │ │ │ │ │ │ │
26
- * │ │ │ │ ◀────────────────────────────────────▶ │ │ │ ▼ │
27
- * └─┴────────────────┴─┴────────────────────────────────────────┴─┴────────────────┴───────┘
28
- *
29
- * ╔════╗
30
- * ║ ║ Sticky
31
- * ╚════╝
32
- * ◀────▶ Scrollable
33
- * ─────▷ Emulated tab order
34
- *
35
- */
36
- import { ReactElement, ReactNode } from "react";
37
- import { IconName } from "./Icon";
38
- import { SpacingValue } from "./Space";
39
- export type ColumnTitleConfig<ExtraInfo> = {
40
- title: string;
41
- extraInfo: ExtraInfo;
42
- id: string;
43
- };
44
- export type ColumnCellConfig<T, ExtraInfo> = {
45
- columnId: string;
46
- item: T;
47
- index: number;
48
- extraInfo: ExtraInfo;
49
- isHovered: boolean;
50
- };
51
- export type LinkConfig<T, ExtraInfo> = {
52
- item: T;
53
- index: number;
54
- extraInfo: ExtraInfo;
55
- };
56
- export type ColumnConfig<T, ExtraInfo> = {
57
- id: string;
58
- width: number;
59
- title: string;
60
- renderTitle: (props: ColumnTitleConfig<ExtraInfo>) => ReactNode;
61
- renderCell: (props: ColumnCellConfig<T, ExtraInfo>) => ReactNode;
62
- };
63
- type Mode = "tile" | "plain";
64
- export type FixedListViewProps<T, ExtraInfo> = {
65
- mode?: Mode;
66
- data: T[];
67
- keyExtractor: (item: T, index: number) => string;
68
- highlightedRowId?: string;
69
- headerBackgroundColor?: string;
70
- headerHeight: number;
71
- rowHeight: number;
72
- rowVerticalSpacing: number;
73
- horizontalPadding?: number;
74
- extraInfo: ExtraInfo;
75
- stickedToStartColumns?: ColumnConfig<T, ExtraInfo>[];
76
- columns: ColumnConfig<T, ExtraInfo>[];
77
- stickedToEndColumns?: ColumnConfig<T, ExtraInfo>[];
78
- renderThreshold?: number;
79
- onEndReached?: () => void;
80
- onEndReachedThresholdPx?: number;
81
- getRowLink?: (config: LinkConfig<T, ExtraInfo>) => ReactElement | undefined;
82
- renderEmptyList?: () => ReactNode;
83
- loading?: {
84
- isLoading: boolean;
85
- count: number;
86
- };
87
- };
88
- export declare const SCROLLBAR_RESERVED_SPACE = 20;
89
- export declare const FixedListView: <T, ExtraInfo>({ data: originalData, mode, keyExtractor, highlightedRowId, rowHeight, rowVerticalSpacing, horizontalPadding, headerBackgroundColor, headerHeight, renderThreshold, stickedToStartColumns: initialStickedToStartColumns, columns: initialColumns, stickedToEndColumns: initialStickedToEndColumns, extraInfo, onEndReached, onEndReachedThresholdPx, getRowLink, renderEmptyList, loading, }: FixedListViewProps<T, ExtraInfo>) => import("react/jsx-runtime").JSX.Element;
90
- type PlaceholderProps = {
91
- count: number;
92
- rowHeight: number;
93
- rowVerticalSpacing: number;
94
- groupHeaderHeight?: number;
95
- headerHeight?: number;
96
- paddingHorizontal?: number;
97
- };
98
- export declare const FixedListViewPlaceholder: ({ count, rowHeight, rowVerticalSpacing, groupHeaderHeight, headerHeight, paddingHorizontal, }: PlaceholderProps) => import("react/jsx-runtime").JSX.Element;
99
- export declare const PlainListViewPlaceholder: ({ count, rowHeight, rowVerticalSpacing, groupHeaderHeight, headerHeight, paddingHorizontal, }: PlaceholderProps) => import("react/jsx-runtime").JSX.Element;
100
- type EmptyProps = {
101
- icon: IconName;
102
- borderedIcon?: boolean;
103
- borderedIconPadding?: SpacingValue | 0;
104
- title?: string;
105
- subtitle?: ReactNode;
106
- children?: ReactNode;
107
- };
108
- export declare const FixedListViewEmpty: ({ icon, borderedIcon, borderedIconPadding, title, subtitle, children, }: EmptyProps) => import("react/jsx-runtime").JSX.Element;
109
- export {};
@@ -1,834 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- /**
3
- * ## FixedListView
4
- *
5
- * The FixedListView is a component designed to render big amounts of tabular data.
6
- *
7
- * For usability, the data can be display in three types of columns:
8
- *
9
- * - Sticked to start columns (usually the main identifier, always visible)
10
- * - Center columns (scrollable columns, with additional information)
11
- * - Sticked to end columns (so that some actions are always accesible at the end of each line)
12
- *
13
- * ┌────────────────────────────────────────────────────────────────────────────────────────┐
14
- * │ ┌────────────────┐ ┌────────────────────────────────────────┐ ┌────────────────┐ │
15
- * │ │ ╔════════════╗ │ │ ╔════════════════════════════════════╗ │ │ ╔════════════╗ │ │
16
- * │ │ ║ Header ║ │ │ ║ Header ║ │ │ ║ Header ║ │ ▲ │
17
- * │ │ ║ ║ │ │ ║◀──────────────────────────────────▶║ │ │ ║ ║ │ │ │
18
- * │ │ ╚════════════╝ │ │ ╚════════════════════════════════════╝ │ │ ╚════════════╝ │ │ │
19
- * │ │ │ │ │ │ │ │ │
20
- * │ │ ┌───────────┐ │ │ ┌────────────────────────────────────┐ │ │ ┌────────────┐ │ │ │
21
- * │ │ │ Cell A1 │──┼─┼▷│ Cell B1 ├─┼─┼─▷ Cell C1 │ │ │ │
22
- * │ │ └───────────┘ │ │ └────────────────────────────────────┘ │ │ └────────────┘ │ │ │
23
- * │ │ ┌───────────┐ │ │ ┌────────────────────────────────────┐ │ │ ┌────────────┐ │ │ │
24
- * │ │ │ Cell A2 │──┼─┼▷│ Cell B2 │─┼─┼─▷ Cell C2 │ │ │ │
25
- * │ │ └───────────┘ │ │ └────────────────────────────────────┘ │ │ └────────────┘ │ │ │
26
- * │ │ │ │ │ │ │ │ │
27
- * │ │ │ │ ◀────────────────────────────────────▶ │ │ │ ▼ │
28
- * └─┴────────────────┴─┴────────────────────────────────────────┴─┴────────────────┴───────┘
29
- *
30
- * ╔════╗
31
- * ║ ║ Sticky
32
- * ╚════╝
33
- * ◀────▶ Scrollable
34
- * ─────▷ Emulated tab order
35
- *
36
- */
37
- import { cloneElement, Fragment, memo, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from "react";
38
- import { StyleSheet, View, } from "react-native";
39
- import { match } from "ts-pattern";
40
- import { v4 as uuid } from "uuid";
41
- import { backgroundColor, colors, invariantColors, radii, shadows, spacings, } from "../constants/design";
42
- import { useHover } from "../hooks/useHover";
43
- import { getFocusableElements } from "../utils/a11y";
44
- import { first, last, sortedIndexOf } from "../utils/array";
45
- import { noop } from "../utils/function";
46
- import { isNotNullish, isNullish } from "../utils/nullish";
47
- import { BorderedIcon } from "./BorderedIcon";
48
- import { Icon } from "./Icon";
49
- import { LakeHeading } from "./LakeHeading";
50
- import { LakeText } from "./LakeText";
51
- import { ScrollView } from "./ScrollView";
52
- import { Space } from "./Space";
53
- const HORIZONTAL_SAFE_AREA = 10;
54
- export const SCROLLBAR_RESERVED_SPACE = 20;
55
- const styles = StyleSheet.create({
56
- root: {
57
- height: 1,
58
- alignSelf: "stretch",
59
- flexGrow: 1,
60
- },
61
- container: {
62
- height: 1,
63
- alignSelf: "stretch",
64
- flexGrow: 1,
65
- },
66
- containerTile: {
67
- marginHorizontal: -HORIZONTAL_SAFE_AREA,
68
- },
69
- contentContainer: {
70
- flexDirection: "row",
71
- alignItems: "stretch",
72
- flexGrow: 1,
73
- },
74
- scrollContentContainer: {
75
- flexGrow: 1,
76
- flexDirection: "row",
77
- alignItems: "stretch",
78
- },
79
- centerColumnsContainer: {
80
- width: 1,
81
- flexGrow: 1,
82
- flexDirection: "column",
83
- alignItems: "stretch",
84
- },
85
- centerColumns: {
86
- flexGrow: 1,
87
- },
88
- centerColumnsContentContainer: {
89
- flexDirection: "column",
90
- flexGrow: 1,
91
- },
92
- stickyColumn: {
93
- flexGrow: 0,
94
- zIndex: 1,
95
- },
96
- stickyColumnStartOverflow: {
97
- position: "absolute",
98
- top: 0,
99
- bottom: 0,
100
- right: "100%",
101
- },
102
- stickyColumnEndOverflow: {
103
- position: "absolute",
104
- top: 0,
105
- bottom: 0,
106
- left: "100%",
107
- },
108
- rowSegment: {
109
- position: "absolute",
110
- left: 0,
111
- right: 0,
112
- display: "flex",
113
- flexGrow: 1,
114
- alignSelf: "stretch",
115
- flexDirection: "row",
116
- alignItems: "stretch",
117
- transitionProperty: "top",
118
- transitionDuration: "300ms",
119
- transitionTimingFunction: "ease-in-out",
120
- overflow: "hidden",
121
- },
122
- headingSegment: {
123
- position: "sticky",
124
- top: 0,
125
- flexDirection: "row",
126
- alignItems: "stretch",
127
- zIndex: 2,
128
- },
129
- segment: {
130
- flexDirection: "row",
131
- alignItems: "stretch",
132
- overflow: "hidden",
133
- },
134
- segmentOverflow: {
135
- overflow: "hidden",
136
- flexDirection: "row",
137
- alignItems: "stretch",
138
- },
139
- visibleGradient: {
140
- opacity: 1,
141
- },
142
- leftToRightGradient: {
143
- position: "absolute",
144
- left: 0,
145
- top: 0,
146
- height: "100%",
147
- width: 10,
148
- backgroundImage: "linear-gradient(to right, rgba(0, 0, 0, 0.06), rgba(0, 0, 0, 0))",
149
- opacity: 0,
150
- transition: "150ms ease-in-out opacity",
151
- pointerEvents: "none",
152
- },
153
- rightToLeftGradient: {
154
- position: "absolute",
155
- right: 0,
156
- top: 0,
157
- height: "100%",
158
- width: 10,
159
- backgroundImage: "linear-gradient(to left, rgba(0, 0, 0, 0.06), rgba(0, 0, 0, 0))",
160
- opacity: 0,
161
- transition: "150ms ease-in-out opacity",
162
- pointerEvents: "none",
163
- },
164
- horizontalScrollbar: {
165
- position: "sticky",
166
- bottom: 0,
167
- borderBottomWidth: 5,
168
- borderBottomColor: invariantColors.transparent,
169
- flexGrow: 0,
170
- height: SCROLLBAR_RESERVED_SPACE,
171
- },
172
- cell: {
173
- flexDirection: "row",
174
- alignItems: "stretch",
175
- },
176
- evenRow: {
177
- backgroundColor: backgroundColor.accented,
178
- },
179
- oddRow: {
180
- backgroundColor: backgroundColor.default,
181
- },
182
- rowShadow: {
183
- boxShadow: shadows.tile,
184
- },
185
- hoveredRowShadow: {
186
- boxShadow: shadows.tileHover,
187
- },
188
- highlightedRow: {
189
- borderColor: colors.current.primary,
190
- borderWidth: 1,
191
- },
192
- segmentHeaderCell: {
193
- display: "flex",
194
- flexDirection: "row",
195
- flexGrow: 1,
196
- alignItems: "center",
197
- },
198
- rowBackground: {
199
- borderRadius: radii[4],
200
- transitionProperty: "top",
201
- transitionDuration: "300ms",
202
- transitionTimingFunction: "ease-in-out",
203
- },
204
- rowPlainBackground: {
205
- transitionProperty: "top",
206
- transitionDuration: "300ms",
207
- transitionTimingFunction: "ease-in-out",
208
- },
209
- rowBackgroundContainer: {
210
- position: "absolute",
211
- left: 0,
212
- right: 0,
213
- display: "flex",
214
- flexDirection: "column",
215
- alignItems: "stretch",
216
- justifyContent: "center",
217
- transitionProperty: "top",
218
- transitionDuration: "300ms",
219
- transitionTimingFunction: "ease-in-out",
220
- },
221
- rowBackgroundContainerPlain: {
222
- left: -10,
223
- right: -10,
224
- boxShadow: `inset 0 -1px ${colors.gray[100]}`,
225
- },
226
- backgroundRows: {
227
- position: "absolute",
228
- left: HORIZONTAL_SAFE_AREA,
229
- right: HORIZONTAL_SAFE_AREA,
230
- top: 0,
231
- bottom: 0,
232
- },
233
- placeholderRowContainer: {
234
- flexDirection: "row",
235
- alignItems: "center",
236
- position: "absolute",
237
- top: 0,
238
- left: 20,
239
- right: 20,
240
- animationKeyframes: {
241
- "50%": {
242
- opacity: 0.6,
243
- },
244
- },
245
- animationDuration: "2000ms",
246
- animationTimingFunction: "linear",
247
- animationIterationCount: "infinite",
248
- },
249
- placeholderRowContainerPlain: {
250
- left: 10,
251
- },
252
- placeholderRow: {
253
- height: 14,
254
- width: "30%",
255
- backgroundColor: colors.gray[200],
256
- borderRadius: radii[6],
257
- },
258
- placeholderRowEnd: {
259
- flexGrow: 1,
260
- flexDirection: "row",
261
- alignItems: "center",
262
- justifyContent: "flex-end",
263
- },
264
- smallPlaceholderRow: {
265
- width: "10%",
266
- },
267
- centerSegmentContainer: {
268
- flexGrow: 1,
269
- },
270
- rowLeftRadii: {
271
- borderTopLeftRadius: radii[4],
272
- borderBottomLeftRadius: radii[4],
273
- },
274
- rowRightRadii: {
275
- borderTopRightRadius: radii[4],
276
- borderBottomRightRadius: radii[4],
277
- },
278
- emptyListContainer: {
279
- position: "absolute",
280
- top: 0,
281
- left: 0,
282
- right: 0,
283
- bottom: 0,
284
- backgroundColor: backgroundColor.default,
285
- },
286
- emptyListContentContainer: {
287
- flexDirection: "column",
288
- alignItems: "center",
289
- justifyContent: "center",
290
- padding: spacings[48],
291
- minHeight: "100%",
292
- },
293
- emptyList: {
294
- flexDirection: "column",
295
- alignItems: "center",
296
- justifyContent: "center",
297
- },
298
- loadingPlaceholder: {
299
- position: "absolute",
300
- left: 0,
301
- right: 0,
302
- },
303
- topGradient: {
304
- height: 30,
305
- position: "absolute",
306
- left: 0,
307
- right: 0,
308
- top: "100%",
309
- backgroundImage: `linear-gradient(to bottom, ${backgroundColor.default}, ${backgroundColor.defaultTransparent})`,
310
- opacity: 0,
311
- transition: "200ms ease-in-out opacity",
312
- pointerEvents: "none",
313
- },
314
- visibleTopGradient: {
315
- opacity: 1,
316
- },
317
- });
318
- const RowBackground = ({ absoluteIndex, id, isHovered, isHighlighted, top, rowHeight, rowVerticalSpacing, onMouseEnter, onMouseLeave, mode, }) => {
319
- const containerRef = useRef(null);
320
- useHover(containerRef, {
321
- onHoverStart: () => onMouseEnter(id),
322
- onHoverEnd: () => onMouseLeave(),
323
- });
324
- return (_jsx(View, { style: [
325
- styles.rowBackgroundContainer,
326
- mode === "plain" && styles.rowBackgroundContainerPlain,
327
- { top, paddingVertical: rowVerticalSpacing / 2 },
328
- ], ref: containerRef, children: mode === "tile" ? (_jsx(View, { style: [
329
- styles.rowBackground,
330
- isHovered ? styles.hoveredRowShadow : styles.rowShadow,
331
- isHighlighted && styles.highlightedRow,
332
- absoluteIndex % 2 === 0 ? styles.evenRow : styles.oddRow,
333
- { height: rowHeight },
334
- ] })) : (_jsx(View, { style: [styles.rowPlainBackground, { height: rowHeight }] })) }));
335
- };
336
- const MemoizedRowBackground = memo(RowBackground);
337
- const SEGMENTS_MAP = {
338
- start: "0",
339
- center: "1",
340
- end: "2",
341
- };
342
- const RowSegment = ({ columns, item, style, absoluteIndex, viewId, segmentColumn, rowVerticalSpacing, width, id, isHovered, extraInfo, onMouseEnter, onMouseLeave, createRowWrapper, focusId, top, totalRowHeight, minWidth, }) => {
343
- const containerRef = useRef(null);
344
- useHover(containerRef, {
345
- onHoverStart: () => onMouseEnter(id),
346
- onHoverEnd: () => onMouseLeave(),
347
- });
348
- const wrapper = createRowWrapper({ item, absoluteIndex, extraInfo });
349
- return cloneElement(wrapper, {
350
- id: focusId,
351
- tabIndex: 0,
352
- style: [
353
- styles.rowSegment,
354
- {
355
- top,
356
- height: totalRowHeight,
357
- width: isNullish(minWidth) ? width : undefined,
358
- minWidth,
359
- },
360
- ],
361
- }, _jsx(View, { style: [
362
- styles.segment,
363
- { width, paddingVertical: rowVerticalSpacing / 2 },
364
- segmentColumn === "center" && styles.centerSegmentContainer,
365
- segmentColumn === "start" && {
366
- borderTopLeftRadius: radii[4],
367
- borderBottomLeftRadius: radii[4],
368
- },
369
- segmentColumn === "end" && {
370
- borderTopRightRadius: radii[4],
371
- borderBottomRightRadius: radii[4],
372
- },
373
- ], ref: containerRef, "aria-hidden": false, children: _jsx(View, { style: [styles.segmentOverflow, style], children: columns.map(({ id, width, renderCell }, index) => {
374
- const columnId = `${viewId}_${id}`;
375
- const paddedIndex = String(absoluteIndex).padStart(10, "0");
376
- const paddedCellIndex = String(index).padStart(10, "0");
377
- // The reason we use this shape is so that the IDs alphabetical order matches the semantical one:
378
- // 1. Row index
379
- // 2. Column index
380
- // 3. Cell index
381
- // -> See the `onKeyDown` handler
382
- const focusId = `${viewId}__Row_${paddedIndex}_Segment_${SEGMENTS_MAP[segmentColumn]}_Cell_${paddedCellIndex}`;
383
- return (_jsx(View, { style: [styles.cell, { width }], "aria-describedby": columnId, id: focusId, children: renderCell({ columnId, item, index: absoluteIndex, extraInfo, isHovered }) }, columnId));
384
- }) }) }));
385
- };
386
- const MemoizedRowSegment = memo(RowSegment);
387
- const HeaderSegment = ({ columns, viewId, extraInfo, width, }) => {
388
- return (_jsx(View, { style: [styles.segment, { width }], children: columns.map(({ id, width, title, renderTitle }) => {
389
- const columnId = `${viewId}_${id}`;
390
- return (_jsx(View, { style: [styles.segmentHeaderCell, { width }], id: columnId, children: renderTitle({ title, extraInfo, id }) }, columnId));
391
- }) }));
392
- };
393
- const findNextFocusableElement = (sortedCellIds, currentCellIndex, direction) => {
394
- let index = currentCellIndex + direction;
395
- while (index >= 0 && index < sortedCellIds.length) {
396
- const nextCellId = sortedCellIds[index];
397
- if (isNotNullish(nextCellId)) {
398
- const previousCell = document.getElementById(nextCellId);
399
- if (isNotNullish(previousCell)) {
400
- const focusableElements = getFocusableElements(previousCell, false);
401
- const nextFocusableElement = direction === -1 ? last(focusableElements) : first(focusableElements);
402
- if (isNotNullish(nextFocusableElement)) {
403
- return nextFocusableElement;
404
- }
405
- }
406
- }
407
- index = index + direction;
408
- }
409
- };
410
- const EMPTY_COLUMNS = [];
411
- const ZERO = 0;
412
- export const FixedListView = ({ data: originalData, mode = "tile", keyExtractor, highlightedRowId, rowHeight, rowVerticalSpacing, horizontalPadding = HORIZONTAL_SAFE_AREA, headerBackgroundColor = backgroundColor.default, headerHeight, renderThreshold = 1000, stickedToStartColumns: initialStickedToStartColumns = EMPTY_COLUMNS, columns: initialColumns, stickedToEndColumns: initialStickedToEndColumns = EMPTY_COLUMNS, extraInfo, onEndReached, onEndReachedThresholdPx = 200, getRowLink, renderEmptyList, loading, }) => {
413
- const [viewId] = useState(() => uuid());
414
- // Those three refs are used to synchronize the horizontal scroll in the center columns
415
- const centerHeadersRef = useRef(null);
416
- const centerColumnsRef = useRef(null);
417
- const horizontalScrollbarRef = useRef(null);
418
- const totalRowHeight = rowHeight + rowVerticalSpacing;
419
- const rowsHeight = originalData.length * totalRowHeight;
420
- const totalHeight = headerHeight + rowsHeight;
421
- // It might seem off to use the range in state instead of storing scroll/layout and deriving it,
422
- // but it saves a lot of render phases by allowing to bail out from rendering when the range doesn't change
423
- const [{ data, range: [renderedRangeStartIndex, renderedRangeEndIndex], }, setDataAndRenderRange,] = useState({ data: originalData, range: [0, 20] });
424
- const startFocusAnchorRef = useRef(null);
425
- const endFocusAnchorRef = useRef(null);
426
- const [hasHorizontalScroll, setHasHorizontalScroll] = useState(false);
427
- const [shouldAvoidStickyColumns, setShouldAvoidStickyColumns] = useState(false);
428
- const [shouldShowStartGradient, setShouldShowStartGradient] = useState(false);
429
- const [shouldShowEndGradient, setShouldShowEndGradient] = useState(true);
430
- const [hoveredRow, setHoveredRow] = useState(undefined);
431
- const currentScrollY = useRef(0);
432
- const lastKnownHeight = useRef(0);
433
- const { stickedToStartColumns, columns, stickedToEndColumns } = useMemo(() => {
434
- if (shouldAvoidStickyColumns) {
435
- return {
436
- stickedToStartColumns: [],
437
- columns: [
438
- ...initialStickedToStartColumns,
439
- ...initialColumns,
440
- ...initialStickedToEndColumns,
441
- ],
442
- stickedToEndColumns: [],
443
- };
444
- }
445
- else {
446
- return {
447
- stickedToStartColumns: initialStickedToStartColumns,
448
- columns: initialColumns,
449
- stickedToEndColumns: initialStickedToEndColumns,
450
- };
451
- }
452
- }, [
453
- initialStickedToStartColumns,
454
- initialColumns,
455
- initialStickedToEndColumns,
456
- shouldAvoidStickyColumns,
457
- ]);
458
- const [isScrolled, setIsScrolled] = useState(false);
459
- const removeHoveredRow = useCallback(() => {
460
- setHoveredRow(undefined);
461
- }, []);
462
- const initialStickedToStartColumnsWidth = useMemo(() => initialStickedToStartColumns.reduce((total, { width }) => total + width, 0), [initialStickedToStartColumns]);
463
- const initialStickedToEndColumnsWidth = useMemo(() => initialStickedToEndColumns.reduce((total, { width }) => total + width, 0), [initialStickedToEndColumns]);
464
- const stickedToStartColumnsWidth = useMemo(() => stickedToStartColumns.reduce((total, { width }) => total + width, 0), [stickedToStartColumns]);
465
- const stickedToEndColumnsWidth = useMemo(() => stickedToEndColumns.reduce((total, { width }) => total + width, 0), [stickedToEndColumns]);
466
- const centerColumnsWidth = useMemo(() => columns.reduce((total, { width }) => total + width, 0), [columns]);
467
- const centerSegmentStyle = useMemo(() => [
468
- stickedToStartColumns.length === 0 && styles.rowLeftRadii,
469
- stickedToEndColumns.length === 0 && styles.rowRightRadii,
470
- ], [stickedToStartColumns, stickedToEndColumns]);
471
- const createRowWrapper = useCallback(({ item, absoluteIndex, extraInfo, }) => {
472
- const customLinkElement = getRowLink === null || getRowLink === void 0 ? void 0 : getRowLink({ item, index: absoluteIndex, extraInfo });
473
- return isNullish(customLinkElement) ? _jsx(View, {}) : customLinkElement;
474
- }, [getRowLink]);
475
- const [backgroundRows, startRows, centerRows, endRows] = useMemo(() => {
476
- const length = Math.max(0, renderedRangeEndIndex - renderedRangeStartIndex);
477
- const backgroundRows = Array(length);
478
- const startRows = Array(length);
479
- const centerRows = Array(length);
480
- const endRows = Array(length);
481
- let index = -1;
482
- while (++index < length) {
483
- const absoluteIndex = renderedRangeStartIndex + index;
484
- const item = data[absoluteIndex];
485
- if (isNullish(item)) {
486
- continue;
487
- }
488
- const key = keyExtractor(item, absoluteIndex);
489
- const top = absoluteIndex * totalRowHeight;
490
- const isHoveredRow = hoveredRow === key;
491
- const isHighlightedRow = highlightedRowId === key;
492
- const paddedIndex = String(absoluteIndex).padStart(10, "0");
493
- const focusId = `${viewId}__Row_${paddedIndex}`;
494
- backgroundRows[index] = (_jsx(MemoizedRowBackground, { mode: mode, isHovered: isHoveredRow, isHighlighted: isHighlightedRow, absoluteIndex: absoluteIndex, top: top, id: key, rowHeight: rowHeight, rowVerticalSpacing: rowVerticalSpacing, onMouseEnter: setHoveredRow, onMouseLeave: removeHoveredRow }, key));
495
- if (stickedToStartColumns.length > 0) {
496
- startRows[index] = (_jsx(MemoizedRowSegment, { createRowWrapper: createRowWrapper, focusId: focusId, top: top, totalRowHeight: totalRowHeight, id: key, style: styles.rowLeftRadii, onMouseEnter: setHoveredRow, onMouseLeave: removeHoveredRow, segmentColumn: "start", columns: stickedToStartColumns, width: stickedToStartColumnsWidth, item: item, absoluteIndex: absoluteIndex, rowVerticalSpacing: rowVerticalSpacing, viewId: viewId, isHovered: isHoveredRow, extraInfo: extraInfo }, key));
497
- }
498
- centerRows[index] = (_jsx(MemoizedRowSegment, { mode: mode, createRowWrapper: createRowWrapper, focusId: focusId, top: top, totalRowHeight: totalRowHeight, minWidth: centerColumnsWidth, id: key, style: centerSegmentStyle, onMouseEnter: setHoveredRow, onMouseLeave: removeHoveredRow, segmentColumn: "center", columns: columns, width: centerColumnsWidth, item: item, absoluteIndex: absoluteIndex, rowVerticalSpacing: rowVerticalSpacing, viewId: viewId, isHovered: isHoveredRow, extraInfo: extraInfo }, key));
499
- if (stickedToEndColumns.length > 0) {
500
- endRows[index] = (_jsx(MemoizedRowSegment, { createRowWrapper: createRowWrapper, focusId: focusId, top: top, totalRowHeight: totalRowHeight, id: key, style: styles.rowLeftRadii, onMouseEnter: setHoveredRow, onMouseLeave: removeHoveredRow, segmentColumn: "end", columns: stickedToEndColumns, width: stickedToEndColumnsWidth, item: item, absoluteIndex: absoluteIndex, rowVerticalSpacing: rowVerticalSpacing, viewId: viewId, isHovered: isHoveredRow, extraInfo: extraInfo }, key));
501
- }
502
- }
503
- return [backgroundRows, startRows, centerRows, endRows];
504
- }, [
505
- data,
506
- renderedRangeStartIndex,
507
- renderedRangeEndIndex,
508
- keyExtractor,
509
- highlightedRowId,
510
- rowHeight,
511
- totalRowHeight,
512
- columns,
513
- stickedToEndColumns,
514
- stickedToStartColumns,
515
- viewId,
516
- stickedToStartColumnsWidth,
517
- centerColumnsWidth,
518
- stickedToEndColumnsWidth,
519
- hoveredRow,
520
- rowVerticalSpacing,
521
- extraInfo,
522
- removeHoveredRow,
523
- centerSegmentStyle,
524
- createRowWrapper,
525
- mode,
526
- ]);
527
- // Used to fix some scrollbar behavior. See `main.css`.
528
- useLayoutEffect(() => {
529
- var _a, _b, _c, _d, _e, _f;
530
- (_b = (_a = centerHeadersRef.current) === null || _a === void 0 ? void 0 : _a.element) === null || _b === void 0 ? void 0 : _b.setAttribute("data-hide-scrollbar", String(true));
531
- (_d = (_c = centerColumnsRef.current) === null || _c === void 0 ? void 0 : _c.element) === null || _d === void 0 ? void 0 : _d.setAttribute("data-hide-scrollbar", String(true));
532
- (_f = (_e = horizontalScrollbarRef.current) === null || _e === void 0 ? void 0 : _e.element) === null || _f === void 0 ? void 0 : _f.setAttribute("data-force-scrollbar", String(true));
533
- }, []);
534
- // To synchronize scrolls, we keep track of the initiator in order to ignore the scroll events
535
- // we provoke ourselves with the sync.
536
- const lastHorizontalScroll = useRef({
537
- initiator: "columns",
538
- date: 0,
539
- });
540
- useEffect(() => {
541
- if (isNotNullish(centerHeadersRef.current) &&
542
- isNotNullish(centerColumnsRef.current) &&
543
- isNotNullish(horizontalScrollbarRef.current)) {
544
- const SCROLL_THRESHOLD_MS = 500;
545
- const centerColumns = centerColumnsRef.current.element;
546
- const centerHeaders = centerHeadersRef.current.element;
547
- const horizontalScrollbar = horizontalScrollbarRef.current.element;
548
- const onColumnsScroll = () => {
549
- const now = Date.now();
550
- if (lastHorizontalScroll.current.initiator === "columns" ||
551
- now - lastHorizontalScroll.current.date > SCROLL_THRESHOLD_MS) {
552
- const scrollLeft = centerColumns.scrollLeft;
553
- setShouldShowStartGradient(scrollLeft > 0);
554
- setShouldShowEndGradient(centerColumns.scrollWidth - horizontalPadding * 2 >=
555
- scrollLeft + centerColumns.clientWidth);
556
- centerHeaders.scrollLeft = scrollLeft;
557
- horizontalScrollbar.scrollLeft = scrollLeft;
558
- lastHorizontalScroll.current = { initiator: "columns", date: now };
559
- }
560
- };
561
- const onHeadersScroll = () => {
562
- const now = Date.now();
563
- if (lastHorizontalScroll.current.initiator === "headers" ||
564
- now - lastHorizontalScroll.current.date > SCROLL_THRESHOLD_MS) {
565
- const scrollLeft = centerHeaders.scrollLeft;
566
- setShouldShowStartGradient(scrollLeft > 0);
567
- setShouldShowEndGradient(centerHeaders.scrollWidth - horizontalPadding * 2 >=
568
- scrollLeft + centerHeaders.clientWidth);
569
- centerColumns.scrollLeft = scrollLeft;
570
- horizontalScrollbar.scrollLeft = scrollLeft;
571
- lastHorizontalScroll.current = { initiator: "headers", date: now };
572
- }
573
- };
574
- const onScrollbarScroll = () => {
575
- const now = Date.now();
576
- if (lastHorizontalScroll.current.initiator === "scrollbar" ||
577
- now - lastHorizontalScroll.current.date > SCROLL_THRESHOLD_MS) {
578
- const scrollLeft = horizontalScrollbar.scrollLeft;
579
- setShouldShowStartGradient(scrollLeft > 0);
580
- setShouldShowEndGradient(horizontalScrollbar.scrollWidth - horizontalPadding * 2 >=
581
- scrollLeft + horizontalScrollbar.clientWidth);
582
- centerHeaders.scrollLeft = scrollLeft;
583
- centerColumns.scrollLeft = scrollLeft;
584
- lastHorizontalScroll.current = { initiator: "scrollbar", date: now };
585
- }
586
- };
587
- centerColumns.addEventListener("scroll", onColumnsScroll, { passive: true });
588
- centerHeaders.addEventListener("scroll", onHeadersScroll, { passive: true });
589
- horizontalScrollbar.addEventListener("scroll", onScrollbarScroll, { passive: true });
590
- return () => {
591
- centerColumns.removeEventListener("scroll", onColumnsScroll);
592
- centerHeaders.removeEventListener("scroll", onHeadersScroll);
593
- horizontalScrollbar.removeEventListener("scroll", onScrollbarScroll);
594
- };
595
- }
596
- }, [horizontalPadding]);
597
- const onKeyDown = useCallback((event) => {
598
- var _a, _b;
599
- const target = event.nativeEvent.target;
600
- const currentTarget = event.nativeEvent.currentTarget;
601
- const currentCell = target.closest(`[id^="${viewId}__Row"]`);
602
- const currentCellId = currentCell === null || currentCell === void 0 ? void 0 : currentCell.id;
603
- if (event.nativeEvent.key === "Tab" && isNotNullish(currentCell)) {
604
- const focusableElements = getFocusableElements(currentCell, false);
605
- const firstFocusableElement = first(focusableElements);
606
- const lastFocusableElement = last(focusableElements);
607
- const sortedCellIds = Array.from(currentTarget.querySelectorAll(`[id^="${viewId}__Row"]`), item => item.id).sort();
608
- const currentCellIndex = sortedIndexOf(sortedCellIds, currentCellId);
609
- const isTargetFirst = isNullish(firstFocusableElement) || firstFocusableElement === target;
610
- const isTargetLast = isNullish(lastFocusableElement) || lastFocusableElement === target;
611
- if (isTargetFirst && event.nativeEvent.shiftKey && first(sortedCellIds) !== currentCellId) {
612
- const lastFocusableElement = findNextFocusableElement(sortedCellIds, currentCellIndex, -1);
613
- if (isNotNullish(lastFocusableElement)) {
614
- event.preventDefault();
615
- lastFocusableElement.focus();
616
- }
617
- else {
618
- event.preventDefault();
619
- (_a = startFocusAnchorRef.current) === null || _a === void 0 ? void 0 : _a.focus();
620
- }
621
- }
622
- if (isTargetLast && !event.nativeEvent.shiftKey && last(sortedCellIds) !== currentCellId) {
623
- const firstFocusableElement = findNextFocusableElement(sortedCellIds, currentCellIndex, 1);
624
- if (isNotNullish(firstFocusableElement)) {
625
- event.preventDefault();
626
- firstFocusableElement.focus();
627
- }
628
- else {
629
- event.preventDefault();
630
- (_b = endFocusAnchorRef.current) === null || _b === void 0 ? void 0 : _b.focus();
631
- }
632
- }
633
- }
634
- }, [viewId]);
635
- useLayoutEffect(() => {
636
- const renderedRangeStartIndex = Math.max(0, Math.floor((currentScrollY.current - renderThreshold) / totalRowHeight));
637
- const renderedRangeEndIndex = Math.min(originalData.length, renderedRangeStartIndex +
638
- Math.ceil((lastKnownHeight.current + renderThreshold * 2) / totalRowHeight));
639
- setDataAndRenderRange(prevRenderRange => {
640
- const { data, range: [prevRenderedRangeStartIndex, prevRenderedRangeEndIndex], } = prevRenderRange;
641
- if (prevRenderedRangeStartIndex === renderedRangeStartIndex &&
642
- prevRenderedRangeEndIndex === renderedRangeEndIndex &&
643
- data === originalData) {
644
- return prevRenderRange;
645
- }
646
- return { data: originalData, range: [renderedRangeStartIndex, renderedRangeEndIndex] };
647
- });
648
- }, [originalData, renderThreshold, totalRowHeight]);
649
- const onLayout = useCallback(({ nativeEvent: { layout: { height, width }, }, }) => {
650
- lastKnownHeight.current = height;
651
- const renderedRangeStartIndex = Math.max(0, Math.floor((currentScrollY.current - renderThreshold) / totalRowHeight));
652
- const renderedRangeEndIndex = Math.min(originalData.length, renderedRangeStartIndex + Math.ceil((height + renderThreshold * 2) / totalRowHeight));
653
- setDataAndRenderRange(prevRenderRange => {
654
- const { data, range: [prevRenderedRangeStartIndex, prevRenderedRangeEndIndex], } = prevRenderRange;
655
- if (prevRenderedRangeStartIndex === renderedRangeStartIndex &&
656
- prevRenderedRangeEndIndex === renderedRangeEndIndex &&
657
- data === originalData) {
658
- return prevRenderRange;
659
- }
660
- return { data: originalData, range: [renderedRangeStartIndex, renderedRangeEndIndex] };
661
- });
662
- if (isNotNullish(onEndReached) &&
663
- !hasEndReachedBeenCalled.current &&
664
- height >= totalHeight - onEndReachedThresholdPx) {
665
- hasEndReachedBeenCalled.current = true;
666
- onEndReached();
667
- }
668
- setShouldAvoidStickyColumns(width - (initialStickedToStartColumnsWidth + initialStickedToEndColumnsWidth) < 300);
669
- }, [
670
- originalData,
671
- renderThreshold,
672
- totalRowHeight,
673
- onEndReached,
674
- onEndReachedThresholdPx,
675
- totalHeight,
676
- initialStickedToStartColumnsWidth,
677
- initialStickedToEndColumnsWidth,
678
- ]);
679
- const scrollTimeoutRef = useRef(undefined);
680
- const scrollContentsRef = useRef(null);
681
- const hasEndReachedBeenCalled = useRef(false);
682
- useEffect(() => {
683
- if (isNotNullish(onEndReached) &&
684
- !hasEndReachedBeenCalled.current &&
685
- lastKnownHeight.current >= totalHeight - onEndReachedThresholdPx) {
686
- hasEndReachedBeenCalled.current = true;
687
- onEndReached();
688
- return;
689
- }
690
- hasEndReachedBeenCalled.current = false;
691
- }, [data, onEndReached, onEndReachedThresholdPx, totalHeight]);
692
- const onScroll = useCallback(({ nativeEvent: { contentOffset: { y }, layoutMeasurement: { height }, contentSize: { height: contentHeight }, }, }) => {
693
- setIsScrolled(y > 0);
694
- lastKnownHeight.current = height;
695
- currentScrollY.current = y;
696
- if (isNotNullish(scrollTimeoutRef.current)) {
697
- clearTimeout(scrollTimeoutRef.current);
698
- }
699
- if (scrollContentsRef.current instanceof HTMLElement) {
700
- scrollContentsRef.current.style.pointerEvents = "none";
701
- }
702
- scrollTimeoutRef.current = window.setTimeout(() => {
703
- if (scrollContentsRef.current instanceof HTMLElement) {
704
- scrollContentsRef.current.style.pointerEvents = "auto";
705
- }
706
- }, 100);
707
- const renderedRangeStartIndex = Math.max(0, Math.floor((currentScrollY.current - renderThreshold) / totalRowHeight));
708
- const renderedRangeEndIndex = Math.min(data.length, renderedRangeStartIndex + Math.ceil((height + renderThreshold * 2) / totalRowHeight));
709
- setDataAndRenderRange(prevRenderRange => {
710
- const { data, range: [prevRenderedRangeStartIndex, prevRenderedRangeEndIndex], } = prevRenderRange;
711
- return prevRenderedRangeStartIndex === renderedRangeStartIndex &&
712
- prevRenderedRangeEndIndex === renderedRangeEndIndex &&
713
- data === data
714
- ? prevRenderRange
715
- : { data, range: [renderedRangeStartIndex, renderedRangeEndIndex] };
716
- });
717
- if (isNotNullish(onEndReached) &&
718
- !hasEndReachedBeenCalled.current &&
719
- y + height >= contentHeight - onEndReachedThresholdPx) {
720
- hasEndReachedBeenCalled.current = true;
721
- onEndReached();
722
- }
723
- }, [data.length, renderThreshold, totalRowHeight, onEndReached, onEndReachedThresholdPx]);
724
- const onCenterTrackLayout = useCallback(({ nativeEvent: { layout: { width }, }, }) => {
725
- setHasHorizontalScroll(centerColumnsWidth > width);
726
- }, [centerColumnsWidth]);
727
- const isLoading = isNotNullish(loading) && loading.isLoading;
728
- return (_jsxs(View, { style: styles.root, children: [_jsx(View, { ref: startFocusAnchorRef, tabIndex: 0 }), _jsxs(ScrollView, { onKeyDown: onKeyDown, onLayout: onLayout, onScroll: onScroll, scrollEventThrottle: 32, style: [styles.container, mode === "tile" && styles.containerTile], contentContainerStyle: [
729
- styles.contentContainer,
730
- {
731
- height: totalHeight +
732
- SCROLLBAR_RESERVED_SPACE +
733
- (isLoading ? loading.count * (rowHeight + rowVerticalSpacing) : 0),
734
- },
735
- ], children: [_jsx(View, { "aria-busy": isLoading, style: [
736
- styles.loadingPlaceholder,
737
- {
738
- top: rowsHeight,
739
- marginLeft: horizontalPadding * 2,
740
- marginRight: horizontalPadding * 2,
741
- },
742
- ], children: isLoading
743
- ? match(mode)
744
- .with("tile", () => (_jsx(FixedListViewPlaceholder, { count: loading.count, headerHeight: headerHeight, rowHeight: rowHeight, rowVerticalSpacing: rowVerticalSpacing, paddingHorizontal: 0 })))
745
- .with("plain", () => (_jsx(PlainListViewPlaceholder, { count: loading.count, headerHeight: headerHeight, rowHeight: rowHeight, rowVerticalSpacing: rowVerticalSpacing, paddingHorizontal: 0 })))
746
- .exhaustive()
747
- : null }), _jsx(View, { style: [styles.backgroundRows, { top: headerHeight }], children: backgroundRows }), _jsxs(View, { style: styles.scrollContentContainer, ref: scrollContentsRef, children: [stickedToStartColumns.length > 0 ? (_jsxs(View, { style: [
748
- styles.stickyColumn,
749
- {
750
- width: stickedToStartColumnsWidth + horizontalPadding,
751
- paddingLeft: horizontalPadding,
752
- },
753
- ], children: [_jsxs(View, { style: [
754
- styles.headingSegment,
755
- { height: headerHeight, backgroundColor: headerBackgroundColor },
756
- ], children: [_jsx(HeaderSegment, { columns: stickedToStartColumns, extraInfo: extraInfo, viewId: viewId, width: stickedToStartColumnsWidth }), _jsx(View, { style: [
757
- styles.stickyColumnStartOverflow,
758
- { width: horizontalPadding, backgroundColor: headerBackgroundColor },
759
- ] }), _jsx(View, { style: [styles.topGradient, isScrolled && styles.visibleTopGradient] })] }), _jsx(View, { style: { height: rowsHeight }, children: startRows })] })) : null, _jsxs(View, { style: [
760
- styles.centerColumnsContainer,
761
- {
762
- paddingLeft: stickedToStartColumns.length === 0 ? horizontalPadding : ZERO,
763
- paddingRight: stickedToEndColumns.length === 0 ? horizontalPadding : ZERO,
764
- },
765
- ], children: [_jsxs(View, { style: [
766
- styles.headingSegment,
767
- { height: headerHeight, backgroundColor: headerBackgroundColor },
768
- ], children: [_jsx(ScrollView, { ref: centerHeadersRef, horizontal: true, onLayout: onCenterTrackLayout, style: styles.centerColumns, contentContainerStyle: {
769
- minWidth: centerColumnsWidth +
770
- (stickedToStartColumns.length === 0 ? horizontalPadding : 0) +
771
- (stickedToEndColumns.length === 0 ? horizontalPadding : 0),
772
- }, children: _jsx(HeaderSegment, { columns: columns, extraInfo: extraInfo, viewId: viewId, width: centerColumnsWidth }) }), _jsx(View, { style: [styles.topGradient, isScrolled && styles.visibleTopGradient] })] }), _jsx(ScrollView, { horizontal: true, ref: centerColumnsRef, style: styles.centerColumns, contentContainerStyle: [
773
- styles.centerColumnsContentContainer,
774
- {
775
- minWidth: centerColumnsWidth +
776
- (stickedToStartColumns.length === 0 ? horizontalPadding : 0) +
777
- (stickedToEndColumns.length === 0 ? horizontalPadding : 0),
778
- },
779
- ], children: centerRows }), _jsx(ScrollView, { ref: horizontalScrollbarRef, horizontal: true, style: styles.horizontalScrollbar, contentContainerStyle: {
780
- minWidth: centerColumnsWidth +
781
- (stickedToStartColumns.length === 0 ? horizontalPadding : 0) +
782
- (stickedToEndColumns.length === 0 ? horizontalPadding : 0),
783
- } }), stickedToStartColumns.length > 0 && hasHorizontalScroll ? (_jsx(View, { style: [
784
- styles.leftToRightGradient,
785
- {
786
- maxHeight: data.length * totalRowHeight,
787
- top: headerHeight,
788
- bottom: SCROLLBAR_RESERVED_SPACE + rowVerticalSpacing / 2,
789
- },
790
- shouldShowStartGradient && styles.visibleGradient,
791
- ] })) : null, stickedToEndColumns.length > 0 && hasHorizontalScroll ? (_jsx(View, { style: [
792
- styles.rightToLeftGradient,
793
- {
794
- maxHeight: data.length * totalRowHeight,
795
- top: headerHeight,
796
- bottom: SCROLLBAR_RESERVED_SPACE + rowVerticalSpacing / 2,
797
- },
798
- shouldShowEndGradient && styles.visibleGradient,
799
- ] })) : null] }), stickedToEndColumns.length > 0 ? (_jsxs(View, { style: [
800
- styles.stickyColumn,
801
- {
802
- width: stickedToEndColumnsWidth + horizontalPadding,
803
- paddingRight: horizontalPadding,
804
- },
805
- ], children: [_jsxs(View, { style: [
806
- styles.headingSegment,
807
- { height: headerHeight, backgroundColor: headerBackgroundColor },
808
- ], children: [_jsx(View, { style: [
809
- styles.stickyColumnEndOverflow,
810
- { width: horizontalPadding, backgroundColor: headerBackgroundColor },
811
- ] }), _jsx(HeaderSegment, { columns: stickedToEndColumns, extraInfo: extraInfo, viewId: viewId, width: stickedToEndColumnsWidth }), _jsx(View, { style: [styles.topGradient, isScrolled && styles.visibleTopGradient] })] }), _jsx(View, { style: { height: rowsHeight }, children: endRows })] })) : null] })] }), data.length === 0 && isNotNullish(renderEmptyList) && !isLoading ? (_jsx(ScrollView, { style: styles.emptyListContainer, contentContainerStyle: styles.emptyListContentContainer, children: renderEmptyList() })) : null, _jsx(View, { ref: endFocusAnchorRef, tabIndex: 0 })] }));
812
- };
813
- export const FixedListViewPlaceholder = ({ count, rowHeight, rowVerticalSpacing, groupHeaderHeight, headerHeight, paddingHorizontal = HORIZONTAL_SAFE_AREA, }) => {
814
- const totalRowHeight = rowHeight + rowVerticalSpacing;
815
- return (_jsxs(View, { style: [styles.container, styles.containerTile], children: [isNotNullish(headerHeight) ? _jsx(View, { style: { height: headerHeight } }) : null, isNotNullish(groupHeaderHeight) ? _jsx(View, { style: { height: headerHeight } }) : null, _jsx(View, { children: Array.from({ length: count }, (_, index) => {
816
- const top = index * totalRowHeight + rowVerticalSpacing / 2;
817
- return (_jsxs(Fragment, { children: [_jsx(MemoizedRowBackground, { isHovered: false, isHighlighted: false, absoluteIndex: index, top: top, rowVerticalSpacing: rowVerticalSpacing, id: String(index), rowHeight: rowHeight, onMouseEnter: noop, onMouseLeave: noop, mode: "tile" }), _jsxs(View, { style: [
818
- styles.placeholderRowContainer,
819
- { height: totalRowHeight, top, paddingHorizontal },
820
- ], children: [_jsx(View, { style: styles.placeholderRow }), _jsx(Space, { width: 32 }), _jsx(View, { style: [styles.placeholderRow, styles.smallPlaceholderRow] }), _jsx(Space, { width: 32 }), _jsx(View, { style: styles.placeholderRowEnd, children: _jsx(View, { style: [styles.placeholderRow, styles.smallPlaceholderRow] }) })] }, String(index))] }, String(index)));
821
- }) })] }));
822
- };
823
- export const PlainListViewPlaceholder = ({ count, rowHeight, rowVerticalSpacing, groupHeaderHeight, headerHeight, paddingHorizontal = HORIZONTAL_SAFE_AREA, }) => {
824
- const totalRowHeight = rowHeight + rowVerticalSpacing;
825
- return (_jsxs(View, { style: styles.container, children: [isNotNullish(headerHeight) ? _jsx(View, { style: { height: headerHeight } }) : null, isNotNullish(groupHeaderHeight) ? _jsx(View, { style: { height: headerHeight } }) : null, _jsx(View, { children: Array.from({ length: count }, (_, index) => {
826
- const top = index * totalRowHeight + rowVerticalSpacing / 2;
827
- return (_jsxs(Fragment, { children: [_jsx(MemoizedRowBackground, { isHovered: false, isHighlighted: false, absoluteIndex: index, top: top, rowVerticalSpacing: rowVerticalSpacing, id: String(index), rowHeight: rowHeight, onMouseEnter: noop, onMouseLeave: noop, mode: "plain" }), _jsxs(View, { style: [
828
- styles.placeholderRowContainer,
829
- styles.placeholderRowContainerPlain,
830
- { height: totalRowHeight, top, paddingHorizontal },
831
- ], children: [_jsx(View, { style: styles.placeholderRow }), _jsx(Space, { width: 32 }), _jsx(View, { style: [styles.placeholderRow, styles.smallPlaceholderRow] }), _jsx(Space, { width: 32 }), _jsx(View, { style: styles.placeholderRowEnd, children: _jsx(View, { style: [styles.placeholderRow, styles.smallPlaceholderRow] }) })] }, String(index))] }, String(index)));
832
- }) })] }));
833
- };
834
- export const FixedListViewEmpty = ({ icon, borderedIcon = false, borderedIconPadding, title, subtitle, children, }) => (_jsxs(View, { style: styles.emptyList, children: [borderedIcon ? (_jsx(BorderedIcon, { name: icon, padding: borderedIconPadding })) : (_jsx(Icon, { name: icon, size: 96, color: colors.current.primary })), _jsx(Space, { height: 24 }), isNotNullish(title) && (_jsxs(_Fragment, { children: [_jsx(LakeHeading, { align: "center", level: 3, variant: "h5", children: title }), _jsx(Space, { height: 8 })] })), isNotNullish(subtitle) && _jsx(LakeText, { align: "center", children: subtitle }), children] }));
@@ -1,68 +0,0 @@
1
- import { ComponentProps, ReactNode } from "react";
2
- import { ColorVariants } from "../constants/design";
3
- import { TextVariant } from "./LakeText";
4
- import { LakeTooltip } from "./LakeTooltip";
5
- type Justify = "flex-start" | "center" | "flex-end";
6
- type SortDirection = "Desc" | "Asc";
7
- export declare const SimpleHeaderCell: ({ text, sort, justifyContent, onPress, }: {
8
- text: string;
9
- justifyContent?: Justify;
10
- sort?: SortDirection;
11
- onPress?: (direction: SortDirection) => void;
12
- }) => import("react/jsx-runtime").JSX.Element;
13
- export declare const ColorPatchCell: ({ isHovered, alternativeText, color, }: {
14
- isHovered: boolean;
15
- alternativeText?: string;
16
- color: ColorVariants;
17
- }) => import("react/jsx-runtime").JSX.Element | null;
18
- export declare const SimpleTitleCell: ({ isHighlighted, text, tooltip, }: {
19
- isHighlighted?: boolean;
20
- text: string;
21
- tooltip?: Omit<ComponentProps<typeof LakeTooltip>, "children">;
22
- }) => import("react/jsx-runtime").JSX.Element;
23
- export declare const SimpleRegularTextCell: ({ variant, text, textAlign, color, }: {
24
- variant?: TextVariant;
25
- text: string;
26
- textAlign?: "left" | "center" | "right";
27
- color?: string;
28
- }) => import("react/jsx-runtime").JSX.Element;
29
- export declare const CopyableRegularTextCell: ({ variant, text, textToCopy, copyWording, copiedWording, tooltip, }: {
30
- variant?: TextVariant;
31
- text: string;
32
- textToCopy?: string;
33
- copyWording: string;
34
- copiedWording: string;
35
- tooltip?: Omit<ComponentProps<typeof LakeTooltip>, "children">;
36
- }) => import("react/jsx-runtime").JSX.Element;
37
- export declare const BalanceCell: ({ value, currency, originalValue, formatCurrency, textAlign, variant, }: {
38
- value: number;
39
- currency: string;
40
- originalValue?: {
41
- value: number;
42
- currency: string;
43
- };
44
- formatCurrency: (value: number, currency: string) => string;
45
- textAlign?: "left" | "center" | "right";
46
- variant?: TextVariant;
47
- }) => import("react/jsx-runtime").JSX.Element;
48
- export declare const LinkCell: ({ children, external, onPress, variant, tooltip, buttonPosition, }: {
49
- children: ReactNode;
50
- onPress: () => void;
51
- external?: boolean;
52
- variant?: TextVariant;
53
- tooltip?: Omit<ComponentProps<typeof LakeTooltip>, "children">;
54
- buttonPosition?: "start" | "end";
55
- }) => import("react/jsx-runtime").JSX.Element;
56
- export declare const StartAlignedCell: ({ children }: {
57
- children: ReactNode;
58
- }) => import("react/jsx-runtime").JSX.Element;
59
- export declare const CenteredCell: ({ children }: {
60
- children: ReactNode;
61
- }) => import("react/jsx-runtime").JSX.Element;
62
- export declare const EndAlignedCell: ({ children }: {
63
- children: ReactNode;
64
- }) => import("react/jsx-runtime").JSX.Element;
65
- export declare const CellAction: ({ children }: {
66
- children: ReactNode;
67
- }) => import("react/jsx-runtime").JSX.Element;
68
- export {};
@@ -1,164 +0,0 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useCallback, useState } from "react";
3
- import { StyleSheet, View } from "react-native";
4
- import { match } from "ts-pattern";
5
- import { visuallyHiddenStyle } from "../constants/commonStyles";
6
- import { colors, spacings } from "../constants/design";
7
- import { setClipboardText } from "../utils/clipboard";
8
- import { isNotNullish, isNullish } from "../utils/nullish";
9
- import { Box } from "./Box";
10
- import { Icon } from "./Icon";
11
- import { LakeText } from "./LakeText";
12
- import { LakeTooltip } from "./LakeTooltip";
13
- import { Pressable } from "./Pressable";
14
- import { Space } from "./Space";
15
- const styles = StyleSheet.create({
16
- cellContainer: {
17
- display: "flex",
18
- flexGrow: 1,
19
- flexDirection: "row",
20
- alignItems: "center",
21
- },
22
- balanceCellContainer: {
23
- width: "100%",
24
- },
25
- cell: {
26
- display: "flex",
27
- paddingHorizontal: spacings[16],
28
- flexGrow: 1,
29
- flexDirection: "row",
30
- alignItems: "center",
31
- },
32
- disabledCellHeader: {
33
- cursor: "text",
34
- },
35
- icon: {
36
- alignSelf: "stretch",
37
- alignItems: "center",
38
- justifyContent: "center",
39
- paddingHorizontal: spacings[4],
40
- },
41
- iconContainer: {
42
- flexDirection: "row",
43
- alignSelf: "stretch",
44
- alignItems: "stretch",
45
- justifyContent: "center",
46
- },
47
- centeredCell: {
48
- justifyContent: "center",
49
- },
50
- endAlignedCell: {
51
- justifyContent: "flex-end",
52
- },
53
- regularText: {
54
- overflow: "hidden",
55
- textOverflow: "ellipsis",
56
- width: 1,
57
- flexGrow: 1,
58
- whiteSpace: "nowrap",
59
- },
60
- mediumText: {
61
- overflow: "hidden",
62
- textOverflow: "ellipsis",
63
- width: 1,
64
- flexGrow: 1,
65
- whiteSpace: "nowrap",
66
- flexDirection: "row",
67
- alignItems: "center",
68
- },
69
- colorPatch: {
70
- flexGrow: 1,
71
- },
72
- alternativeText: visuallyHiddenStyle,
73
- sortIcon: {
74
- transitionProperty: "transform",
75
- transitionDuration: "300ms",
76
- transitionTimingFunction: "ease-in-out",
77
- },
78
- sortIconReversed: {
79
- transform: "rotate(-180deg)",
80
- },
81
- cellAction: {
82
- paddingVertical: spacings[16],
83
- paddingHorizontal: spacings[8],
84
- },
85
- underline: {
86
- boxShadow: "inset 0 -2px currentColor",
87
- },
88
- sortHorizontalBar: {
89
- position: "absolute",
90
- width: "100%",
91
- height: 2,
92
- bottom: -10,
93
- backgroundColor: colors.current[500],
94
- borderBottomColor: colors.current[500],
95
- },
96
- });
97
- export const SimpleHeaderCell = ({ text, sort, justifyContent = "flex-start", onPress, }) => {
98
- const sortActive = isNotNullish(sort) && isNotNullish(onPress);
99
- const disabled = isNullish(onPress);
100
- return (_jsx(Pressable, { onPress: () => {
101
- onPress === null || onPress === void 0 ? void 0 : onPress(match(sort)
102
- .returnType()
103
- .with("Desc", () => "Asc")
104
- .with("Asc", () => "Desc")
105
- .otherwise(() => "Desc"));
106
- }, disabled: disabled, style: [styles.cellContainer, disabled && styles.disabledCellHeader], role: "columnheader", children: ({ hovered }) => (_jsx(View, { style: [styles.cell, { justifyContent }], children: _jsxs(View, { children: [_jsxs(Box, { direction: "row", alignItems: "center", children: [_jsx(LakeText, { numberOfLines: 1, variant: "medium", color: sortActive ? colors.current[500] : colors.gray[900], style: {
107
- textAlign: match(justifyContent)
108
- .with("flex-start", () => "left")
109
- .with("center", () => "center")
110
- .with("flex-end", () => "right")
111
- .exhaustive(),
112
- }, children: text }), isNotNullish(onPress) ? (_jsxs(_Fragment, { children: [_jsx(Space, { width: 8 }), _jsx(Box, { style: [styles.sortIcon, sort === "Asc" && styles.sortIconReversed], children: _jsx(Icon, { size: 15, color: sortActive ? colors.current[500] : colors.gray[500], name: sortActive ? "arrow-down-filled" : "chevron-up-down-regular" }) })] })) : null] }), sortActive ? (_jsx(View, { style: styles.sortHorizontalBar })) : hovered ? (_jsx(View, { style: [styles.sortHorizontalBar, { backgroundColor: colors.gray[900] }] })) : null] }) })) }));
113
- };
114
- export const ColorPatchCell = ({ isHovered, alternativeText, color, }) => {
115
- return isHovered ? (_jsx(View, { style: [styles.colorPatch, { backgroundColor: colors[color].primary }], children: isNotNullish(alternativeText) ? (_jsx(LakeText, { style: styles.alternativeText, children: alternativeText })) : null })) : null;
116
- };
117
- export const SimpleTitleCell = ({ isHighlighted = false, text, tooltip, }) => (_jsx(View, { style: styles.cell, children: _jsx(LakeText, { numberOfLines: 1, color: isHighlighted ? colors.current.primary : colors.gray[900], style: styles.regularText, variant: "medium", tooltip: tooltip, children: text }) }));
118
- export const SimpleRegularTextCell = ({ variant = "regular", text, textAlign = "left", color = colors.gray[900], }) => {
119
- return (_jsx(View, { style: styles.cell, children: _jsx(LakeText, { align: textAlign, color: color, style: styles.regularText, variant: variant, children: text }) }));
120
- };
121
- export const CopyableRegularTextCell = ({ variant = "regular", text, textToCopy, copyWording, copiedWording, tooltip, }) => {
122
- const [visibleState, setVisibleState] = useState("copy");
123
- const clipboardText = textToCopy !== null && textToCopy !== void 0 ? textToCopy : text;
124
- const onPress = useCallback((event) => {
125
- event.preventDefault();
126
- setClipboardText(clipboardText);
127
- setVisibleState("copied");
128
- }, [clipboardText]);
129
- return (_jsxs(View, { style: styles.cell, children: [_jsx(LakeTooltip, { placement: "right", onHide: () => setVisibleState("copy"), togglableOnFocus: true, content: visibleState === "copy" ? copyWording : copiedWording, containerStyle: styles.iconContainer, children: _jsx(Pressable, { role: "button", "aria-label": copyWording, onPress: onPress, style: ({ hovered }) => [styles.icon, hovered && styles.underline], children: ({ hovered }) => (_jsx(Icon, { name: hovered ? "copy-filled" : "copy-regular", color: "currentColor", size: 14 })) }) }), _jsx(Space, { width: 4 }), _jsx(LakeText, { tooltip: tooltip, color: colors.gray[900], style: styles.regularText, variant: variant, children: text })] }));
130
- };
131
- // TODO: handle `+` sign properly
132
- export const BalanceCell = ({ value, currency, originalValue, formatCurrency, textAlign = "right", variant = "medium", }) => {
133
- return (_jsxs(View, { style: styles.balanceCellContainer, children: [_jsx(View, { style: styles.cell, children: _jsxs(LakeText, { align: textAlign, color: colors.gray[900], variant: variant, style: [
134
- styles.mediumText,
135
- {
136
- justifyContent: match(textAlign)
137
- .with("left", () => "flex-start")
138
- .with("center", () => "center")
139
- .with("right", () => "flex-end")
140
- .exhaustive(),
141
- },
142
- value > 0 && { color: colors.positive.primary },
143
- value < 0 && { color: colors.negative.primary },
144
- ], children: [value > 0 && "+", formatCurrency(value, currency)] }) }), isNotNullish(originalValue) && originalValue.currency !== currency && (_jsx(View, { style: styles.cell, children: _jsxs(LakeText, { style: styles.mediumText, align: textAlign, color: colors.gray[500], variant: "smallRegular", children: [originalValue.value > 0 && "+", formatCurrency(originalValue.value, originalValue.currency)] }) }))] }));
145
- };
146
- export const LinkCell = ({ children, external = false, onPress, variant = "medium", tooltip, buttonPosition = "start", }) => {
147
- const ArrowButton = () => (_jsx(Pressable, { style: ({ hovered }) => [styles.icon, hovered && styles.underline], onPress: event => {
148
- event.preventDefault();
149
- onPress();
150
- }, children: _jsx(Icon, { size: 14, name: external ? "open-regular" : "arrow-right-filled" }) }));
151
- return (_jsxs(View, { style: styles.cell, children: [buttonPosition === "start" && (_jsxs(_Fragment, { children: [_jsx(ArrowButton, {}), _jsx(Space, { width: 8 })] })), _jsx(LakeText, { color: colors.gray[900], variant: variant, style: styles.mediumText, tooltip: tooltip, children: children }), buttonPosition === "end" && (_jsxs(_Fragment, { children: [_jsx(Space, { width: 8 }), _jsx(ArrowButton, {})] }))] }));
152
- };
153
- export const StartAlignedCell = ({ children }) => {
154
- return _jsx(View, { style: styles.cell, children: children });
155
- };
156
- export const CenteredCell = ({ children }) => {
157
- return _jsx(View, { style: [styles.cell, styles.centeredCell], children: children });
158
- };
159
- export const EndAlignedCell = ({ children }) => {
160
- return _jsx(View, { style: [styles.cell, styles.endAlignedCell], children: children });
161
- };
162
- export const CellAction = ({ children }) => {
163
- return _jsx(View, { style: styles.cellAction, children: children });
164
- };