@swan-io/lake 8.18.2 → 8.18.4

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.2",
3
+ "version": "8.18.4",
4
4
  "engines": {
5
5
  "node": ">=20.9.0",
6
6
  "yarn": "^1.22.0"
@@ -7,6 +7,7 @@ import { backgroundColor as backgroundColorVariants, colors, spacings } from "..
7
7
  import { useHover } from "../hooks/useHover";
8
8
  import { ScrollView } from "./ScrollView";
9
9
  import { Space } from "./Space";
10
+ const HORIZONTAL_ROW_PADDING = 8;
10
11
  const styles = StyleSheet.create({
11
12
  container: {
12
13
  ...commonStyles.fill,
@@ -29,11 +30,17 @@ const styles = StyleSheet.create({
29
30
  left: 0,
30
31
  zIndex: 1,
31
32
  },
33
+ stickedToStartColumnGroupLocked: {
34
+ position: "relative",
35
+ },
32
36
  stickedToEndColumnGroup: {
33
37
  position: "sticky",
34
38
  right: 0,
35
39
  zIndex: 1,
36
40
  },
41
+ stickedToEndColumnGroupLocked: {
42
+ position: "relative",
43
+ },
37
44
  rowsContainer: {
38
45
  position: "relative",
39
46
  },
@@ -43,6 +50,10 @@ const styles = StyleSheet.create({
43
50
  right: 0,
44
51
  flexDirection: "row",
45
52
  alignItems: "stretch",
53
+ boxShadow: `0 -1px ${colors.gray[100]}`,
54
+ transitionProperty: "top",
55
+ transitionDuration: "300ms",
56
+ transitionTimingFunction: "ease-in-out",
46
57
  },
47
58
  headerCell: {
48
59
  display: "flex",
@@ -55,7 +66,6 @@ const styles = StyleSheet.create({
55
66
  flexDirection: "row",
56
67
  flexGrow: 1,
57
68
  alignItems: "stretch",
58
- boxShadow: `inset 0 -1px ${colors.gray[100]}`,
59
69
  },
60
70
  shadowsLayerContainer: {
61
71
  position: "absolute",
@@ -153,13 +163,27 @@ export const VirtualizedList = ({ variant, data, stickedToStartColumns, columns,
153
163
  .flatMap(({ isLoading, count }) => (isLoading ? Option.Some(rowHeight * count) : Option.None()))
154
164
  .getOr(0);
155
165
  const containerContainerHeight = headerHeight + fullDataHeight + loadingDataPlaceholderHeight;
166
+ const stickedToStartFirstCellLeftPadding = Option.fromNullable(stickedToStartColumns)
167
+ .map(() => HORIZONTAL_ROW_PADDING)
168
+ .getOr(0);
169
+ const centerFirstCellLeftPadding = Option.fromNullable(stickedToStartColumns)
170
+ .map(() => 0)
171
+ .getOr(HORIZONTAL_ROW_PADDING);
172
+ const centerLastCellLeftPadding = Option.fromNullable(stickedToEndColumns)
173
+ .map(() => 0)
174
+ .getOr(HORIZONTAL_ROW_PADDING);
175
+ const stickedToEndLastCellRightPadding = Option.fromNullable(stickedToEndColumns)
176
+ .map(() => HORIZONTAL_ROW_PADDING)
177
+ .getOr(0);
156
178
  const stickedToStartColumnsWidth = useMemo(() => Option.fromNullable(stickedToStartColumns)
157
179
  .map(columns => columns.reduce((acc, column) => acc + column.width, 0))
158
- .getOr(0), [stickedToStartColumns]);
159
- const centerColumnsWidth = useMemo(() => columns.reduce((acc, column) => acc + column.width, 0), [columns]);
180
+ .getOr(0) + stickedToStartFirstCellLeftPadding, [stickedToStartColumns, stickedToStartFirstCellLeftPadding]);
181
+ const centerColumnsWidth = useMemo(() => columns.reduce((acc, column) => acc + column.width, 0) +
182
+ centerFirstCellLeftPadding +
183
+ centerLastCellLeftPadding, [columns, centerFirstCellLeftPadding, centerLastCellLeftPadding]);
160
184
  const stickedToEndColumnsWidth = useMemo(() => Option.fromNullable(stickedToEndColumns)
161
185
  .map(columns => columns.reduce((acc, column) => acc + column.width, 0))
162
- .getOr(0), [stickedToEndColumns]);
186
+ .getOr(0) + stickedToEndLastCellRightPadding, [stickedToEndColumns, stickedToEndLastCellRightPadding]);
163
187
  const contentContainerWidth = stickedToStartColumnsWidth + centerColumnsWidth + stickedToEndColumnsWidth;
164
188
  const backgroundColor = backgroundColorVariants[variant];
165
189
  // We store the `startIndex` and `endIndex` rather than the scroll position
@@ -168,18 +192,21 @@ export const VirtualizedList = ({ variant, data, stickedToStartColumns, columns,
168
192
  const [clientHeight, setClientHeight] = useState(undefined);
169
193
  const [horizontalScrollPosition, setHasHorizontalScrollPosition] = useState(undefined);
170
194
  const rowsToRender = useMemo(() => {
171
- return Option.fromNullable(rangeToRender).map(({ startIndex, endIndex }) => ({
172
- startIndex,
173
- endIndex,
174
- data: data.slice(startIndex, endIndex),
175
- }));
195
+ return Option.fromNullable(rangeToRender).map(({ startIndex, endIndex }) => {
196
+ const clampedEndIndex = Math.min(data.length, endIndex);
197
+ return {
198
+ startIndex,
199
+ endIndex: clampedEndIndex,
200
+ data: data.slice(startIndex, clampedEndIndex),
201
+ };
202
+ });
176
203
  }, [data, rangeToRender]);
177
- useLayoutEffect(() => {
204
+ const onLayoutUpdate = useCallback(() => {
178
205
  const element = Option.fromNullable(scrollViewRef.current).flatMap(ref => Option.fromNullable(ref.element));
179
206
  setRangeToRender(previousRangeToRender => element
180
207
  .map(scrollView => {
181
208
  const startIndex = Math.max(0, Math.floor((scrollView.scrollTop - renderThreshold) / rowHeight));
182
- const endIndex = Math.min(data.length, startIndex + Math.ceil((scrollView.scrollHeight + renderThreshold * 2) / rowHeight));
209
+ const endIndex = startIndex + Math.ceil((scrollView.scrollHeight + renderThreshold * 2) / rowHeight);
183
210
  if ((previousRangeToRender === null || previousRangeToRender === void 0 ? void 0 : previousRangeToRender.startIndex) === startIndex &&
184
211
  previousRangeToRender.endIndex === endIndex) {
185
212
  return previousRangeToRender;
@@ -189,7 +216,8 @@ export const VirtualizedList = ({ variant, data, stickedToStartColumns, columns,
189
216
  .toUndefined());
190
217
  setClientHeight(element.map(scrollView => scrollView.clientHeight).toUndefined());
191
218
  setHasHorizontalScrollPosition(element
192
- .map(scrollView => scrollView.scrollWidth === scrollView.clientWidth
219
+ .map(scrollView => scrollView.scrollWidth === scrollView.clientWidth ||
220
+ scrollView.clientWidth - (stickedToEndColumnsWidth + stickedToStartColumnsWidth) < 400
193
221
  ? "NoScroll"
194
222
  : scrollView.scrollLeft <= 0
195
223
  ? "Start"
@@ -197,11 +225,13 @@ export const VirtualizedList = ({ variant, data, stickedToStartColumns, columns,
197
225
  ? "End"
198
226
  : "Middle")
199
227
  .toUndefined());
200
- }, [data, renderThreshold, rowHeight]);
228
+ }, [data, renderThreshold, rowHeight, stickedToStartColumnsWidth, stickedToEndColumnsWidth]);
229
+ useLayoutEffect(() => {
230
+ onLayoutUpdate();
231
+ }, [onLayoutUpdate]);
201
232
  const scrollTimeoutRef = useRef(undefined);
202
233
  const rowsContainerRef = useRef(null);
203
234
  const onScroll = useCallback(() => {
204
- const element = Option.fromNullable(scrollViewRef.current).flatMap(ref => Option.fromNullable(ref.element));
205
235
  // Disable interactions in cells during scroll, avoids useless
206
236
  // re-renders
207
237
  if (scrollTimeoutRef.current != null) {
@@ -215,27 +245,20 @@ export const VirtualizedList = ({ variant, data, stickedToStartColumns, columns,
215
245
  rowsContainerRef.current.style.pointerEvents = "auto";
216
246
  }
217
247
  }, 100);
218
- setRangeToRender(previousRangeToRender => element
219
- .map(scrollView => {
220
- const startIndex = Math.max(0, Math.floor((scrollView.scrollTop - renderThreshold) / rowHeight));
221
- const endIndex = Math.min(data.length, startIndex + Math.ceil((scrollView.scrollHeight + renderThreshold * 2) / rowHeight));
222
- if ((previousRangeToRender === null || previousRangeToRender === void 0 ? void 0 : previousRangeToRender.startIndex) === startIndex &&
223
- previousRangeToRender.endIndex === endIndex) {
224
- return previousRangeToRender;
225
- }
226
- return { startIndex, endIndex };
248
+ onLayoutUpdate();
249
+ }, [onLayoutUpdate]);
250
+ useEffect(() => {
251
+ const element = Option.fromNullable(scrollViewRef.current).flatMap(ref => Option.fromNullable(ref.element));
252
+ return element
253
+ .map(element => {
254
+ const resizeObserver = new ResizeObserver(() => {
255
+ onLayoutUpdate();
256
+ });
257
+ resizeObserver.observe(element);
258
+ return () => resizeObserver.unobserve(element);
227
259
  })
228
- .toUndefined());
229
- setHasHorizontalScrollPosition(element
230
- .map(scrollView => scrollView.scrollWidth === scrollView.clientWidth
231
- ? "NoScroll"
232
- : scrollView.scrollLeft <= 0
233
- ? "Start"
234
- : scrollView.scrollLeft >= scrollView.scrollWidth - scrollView.clientWidth
235
- ? "End"
236
- : "Middle")
237
- .toUndefined());
238
- }, [data, renderThreshold, rowHeight]);
260
+ .toUndefined();
261
+ }, [onLayoutUpdate]);
239
262
  // tracks if the threshold to initiate the next data load is reached
240
263
  useEffect(() => {
241
264
  const scrollTracker = scrollTrackerRef.current;
@@ -259,22 +282,31 @@ export const VirtualizedList = ({ variant, data, stickedToStartColumns, columns,
259
282
  .map(columns => (_jsx(View, { style: [
260
283
  styles.cellsContainer,
261
284
  styles.stickedToStartColumnGroup,
285
+ horizontalScrollPosition === "NoScroll" && styles.stickedToStartColumnGroupLocked,
262
286
  { width: stickedToStartColumnsWidth, backgroundColor },
263
- ], children: columns.map(({ id, width, title, renderTitle }) => {
287
+ ], children: columns.map(({ id, width, title, renderTitle }, index) => {
264
288
  const columnId = `${viewId}_${id}`;
265
- return (_jsx(View, { style: [styles.headerCell, { width }], id: columnId, children: renderTitle({ title, extraInfo, id }) }, columnId));
289
+ const paddingLeft = index === 0 ? stickedToStartFirstCellLeftPadding : 0;
290
+ return (_jsx(View, { style: [styles.headerCell, { width: width + paddingLeft, paddingLeft }], id: columnId, children: renderTitle({ title, extraInfo, id }) }, columnId));
266
291
  }) })))
267
- .toNull(), _jsx(View, { style: [styles.cellsContainer, { width: centerColumnsWidth, backgroundColor }], children: columns.map(({ id, width, title, renderTitle }) => {
292
+ .toNull(), _jsx(View, { style: [styles.cellsContainer, { width: centerColumnsWidth, backgroundColor }], children: columns.map(({ id, width, title, renderTitle }, index) => {
268
293
  const columnId = `${viewId}_${id}`;
269
- return (_jsx(View, { style: [styles.headerCell, { width }], id: columnId, children: renderTitle({ title, extraInfo, id }) }, columnId));
294
+ const paddingLeft = index === 0 ? centerFirstCellLeftPadding : 0;
295
+ const paddingRight = index === columns.length - 1 ? centerLastCellLeftPadding : 0;
296
+ return (_jsx(View, { style: [
297
+ styles.headerCell,
298
+ { width: width + paddingLeft + paddingRight, paddingLeft, paddingRight },
299
+ ], id: columnId, children: renderTitle({ title, extraInfo, id }) }, columnId));
270
300
  }) }), Option.fromNullable(stickedToEndColumns)
271
301
  .map(columns => (_jsx(View, { style: [
272
302
  styles.cellsContainer,
273
303
  styles.stickedToEndColumnGroup,
304
+ horizontalScrollPosition === "NoScroll" && styles.stickedToEndColumnGroupLocked,
274
305
  { width: stickedToEndColumnsWidth, backgroundColor },
275
- ], children: columns.map(({ id, width, title, renderTitle }) => {
306
+ ], children: columns.map(({ id, width, title, renderTitle }, index) => {
276
307
  const columnId = `${viewId}_${id}`;
277
- return (_jsx(View, { style: [styles.headerCell, { width }], id: columnId, children: renderTitle({ title, extraInfo, id }) }, columnId));
308
+ const paddingRight = index === columns.length - 1 ? stickedToEndLastCellRightPadding : 0;
309
+ return (_jsx(View, { style: [styles.headerCell, { width: width + paddingRight, paddingRight }], id: columnId, children: renderTitle({ title, extraInfo, id }) }, columnId));
278
310
  }) })))
279
311
  .toNull()] }));
280
312
  }, [
@@ -288,6 +320,11 @@ export const VirtualizedList = ({ variant, data, stickedToStartColumns, columns,
288
320
  columns,
289
321
  stickedToEndColumns,
290
322
  viewId,
323
+ horizontalScrollPosition,
324
+ stickedToStartFirstCellLeftPadding,
325
+ centerFirstCellLeftPadding,
326
+ centerLastCellLeftPadding,
327
+ stickedToEndLastCellRightPadding,
291
328
  ]);
292
329
  const startColumnShadow = useMemo(() => {
293
330
  if (stickedToStartColumnsWidth === 0) {
@@ -321,7 +358,7 @@ export const VirtualizedList = ({ variant, data, stickedToStartColumns, columns,
321
358
  height: containerContainerHeight,
322
359
  width: contentContainerWidth,
323
360
  }, children: [header, rowsToRender
324
- .map(({ startIndex, endIndex, data }) => (_jsxs(View, { style: styles.rowsContainer, ref: rowsContainerRef, children: [data.map((item, index) => (_jsx(VirtualizedRow, { viewId: viewId, item: item, rowHeight: rowHeight, absoluteIndex: startIndex + index, variant: variant, stickedToStartColumnsWidth: stickedToStartColumnsWidth, centerColumnsWidth: centerColumnsWidth, stickedToEndColumnsWidth: stickedToEndColumnsWidth, stickedToStartColumns: stickedToStartColumns, columns: columns, stickedToEndColumns: stickedToEndColumns, extraInfo: extraInfo }, keyExtractor(item, startIndex + index)))), Option.fromNullable(loading)
361
+ .map(({ startIndex, endIndex, data }) => (_jsxs(View, { style: styles.rowsContainer, ref: rowsContainerRef, children: [data.map((item, index) => (_jsx(VirtualizedRow, { viewId: viewId, item: item, rowHeight: rowHeight, absoluteIndex: startIndex + index, variant: variant, stickedToStartColumnsWidth: stickedToStartColumnsWidth, centerColumnsWidth: centerColumnsWidth, stickedToEndColumnsWidth: stickedToEndColumnsWidth, stickedToStartColumns: stickedToStartColumns, columns: columns, stickedToEndColumns: stickedToEndColumns, extraInfo: extraInfo, horizontalScrollPosition: horizontalScrollPosition !== null && horizontalScrollPosition !== void 0 ? horizontalScrollPosition : "NoScroll", stickedToStartFirstCellLeftPadding: stickedToStartFirstCellLeftPadding, centerFirstCellLeftPadding: centerFirstCellLeftPadding, centerLastCellLeftPadding: centerLastCellLeftPadding, stickedToEndLastCellRightPadding: stickedToEndLastCellRightPadding }, keyExtractor(item, startIndex + index)))), Option.fromNullable(loading)
325
362
  .flatMap(({ isLoading, count }) => (isLoading ? Option.Some(count) : Option.None()))
326
363
  .map(count => (_jsx(View, { "aria-busy": true, style: [
327
364
  styles.loadingPlaceholder,
@@ -334,7 +371,7 @@ export const VirtualizedList = ({ variant, data, stickedToStartColumns, columns,
334
371
  .map(clientHeight => (_jsx(View, { style: styles.shadowsLayerContainer, children: _jsxs(View, { style: [styles.shadowsLayer, { height: clientHeight - 12 }], children: [startColumnShadow.toNull(), _jsx(View, { style: { width: centerColumnsWidth } }), endColumnShadow.toNull()] }) })))
335
372
  .toNull(), _jsx(View, { style: [styles.scrollTracker, { height: onEndReachedThreshold }], ref: scrollTrackerRef })] }));
336
373
  };
337
- const RawVirtualizedRow = ({ viewId, rowHeight, absoluteIndex, variant, stickedToStartColumnsWidth, centerColumnsWidth, stickedToEndColumnsWidth, stickedToStartColumns, columns, stickedToEndColumns, extraInfo, item, getRowLink, }) => {
374
+ const RawVirtualizedRow = ({ viewId, rowHeight, absoluteIndex, variant, stickedToStartColumnsWidth, centerColumnsWidth, stickedToEndColumnsWidth, stickedToStartColumns, columns, stickedToEndColumns, extraInfo, item, horizontalScrollPosition, getRowLink, stickedToStartFirstCellLeftPadding, centerFirstCellLeftPadding, centerLastCellLeftPadding, stickedToEndLastCellRightPadding, }) => {
338
375
  var _a;
339
376
  const [isHovered, setIsHovered] = useState(false);
340
377
  const elementRef = useRef(null);
@@ -347,30 +384,32 @@ const RawVirtualizedRow = ({ viewId, rowHeight, absoluteIndex, variant, stickedT
347
384
  ref: elementRef,
348
385
  style: [
349
386
  styles.row,
350
- isHovered && {
351
- backgroundColor: variant === "accented"
352
- ? backgroundColorVariants.default
353
- : backgroundColorVariants.accented,
354
- },
355
387
  {
356
388
  backgroundColor: backgroundColorVariants[variant],
357
389
  top: absoluteIndex * rowHeight,
358
390
  height: rowHeight,
359
391
  },
392
+ isHovered && {
393
+ backgroundColor: variant === "accented"
394
+ ? backgroundColorVariants.default
395
+ : backgroundColorVariants.accented,
396
+ },
360
397
  ],
361
398
  children: (_jsxs(_Fragment, { children: [Option.fromNullable(stickedToStartColumns)
362
399
  .map(columns => (_jsx(View, { style: [
363
400
  styles.cellsContainer,
364
401
  styles.stickedToStartColumnGroup,
402
+ horizontalScrollPosition === "NoScroll" && styles.stickedToStartColumnGroupLocked,
365
403
  {
366
404
  width: stickedToStartColumnsWidth,
367
405
  backgroundColor: isHovered
368
406
  ? backgroundColorVariants[variant === "accented" ? "default" : "accented"]
369
407
  : backgroundColorVariants[variant],
370
408
  },
371
- ], children: columns.map(({ id, width, renderCell }) => {
409
+ ], children: columns.map(({ id, width, renderCell }, index) => {
372
410
  const columnId = `${viewId}_${id}`;
373
- return (_jsx(View, { style: [styles.cell, { width }], "aria-describedby": columnId, children: renderCell({
411
+ const paddingLeft = index === 0 ? stickedToStartFirstCellLeftPadding : 0;
412
+ return (_jsx(View, { style: [styles.cell, { width: width + paddingLeft, paddingLeft }], "aria-describedby": columnId, children: renderCell({
374
413
  columnId,
375
414
  item,
376
415
  index: absoluteIndex,
@@ -386,9 +425,14 @@ const RawVirtualizedRow = ({ viewId, rowHeight, absoluteIndex, variant, stickedT
386
425
  ? backgroundColorVariants[variant === "accented" ? "default" : "accented"]
387
426
  : backgroundColorVariants[variant],
388
427
  },
389
- ], children: columns.map(({ id, width, renderCell }) => {
428
+ ], children: columns.map(({ id, width, renderCell }, index) => {
390
429
  const columnId = `${viewId}_${id}`;
391
- return (_jsx(View, { style: [styles.cell, { width }], "aria-describedby": columnId, children: renderCell({
430
+ const paddingLeft = index === 0 ? centerFirstCellLeftPadding : 0;
431
+ const paddingRight = index === columns.length - 1 ? centerLastCellLeftPadding : 0;
432
+ return (_jsx(View, { style: [
433
+ styles.cell,
434
+ { width: width + paddingLeft + paddingRight, paddingLeft, paddingRight },
435
+ ], "aria-describedby": columnId, children: renderCell({
392
436
  columnId,
393
437
  item,
394
438
  index: absoluteIndex,
@@ -399,15 +443,17 @@ const RawVirtualizedRow = ({ viewId, rowHeight, absoluteIndex, variant, stickedT
399
443
  .map(columns => (_jsx(View, { style: [
400
444
  styles.cellsContainer,
401
445
  styles.stickedToEndColumnGroup,
446
+ horizontalScrollPosition === "NoScroll" && styles.stickedToEndColumnGroupLocked,
402
447
  {
403
448
  width: stickedToEndColumnsWidth,
404
449
  backgroundColor: isHovered
405
450
  ? backgroundColorVariants[variant === "accented" ? "default" : "accented"]
406
451
  : backgroundColorVariants[variant],
407
452
  },
408
- ], children: columns.map(({ id, width, renderCell }) => {
453
+ ], children: columns.map(({ id, width, renderCell }, index) => {
409
454
  const columnId = `${viewId}_${id}`;
410
- return (_jsx(View, { style: [styles.cell, { width }], "aria-describedby": columnId, children: renderCell({
455
+ const paddingRight = index === columns.length - 1 ? stickedToEndLastCellRightPadding : 0;
456
+ return (_jsx(View, { style: [styles.cell, { width: width + paddingRight, paddingRight }], "aria-describedby": columnId, children: renderCell({
411
457
  columnId,
412
458
  item,
413
459
  index: absoluteIndex,