@swan-io/lake 8.6.0 → 8.7.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.6.0",
3
+ "version": "8.7.0",
4
4
  "engines": {
5
5
  "node": ">=20.9.0",
6
6
  "yarn": "^1.22.0"
@@ -1,12 +1,13 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useRef, useState } from "react";
3
- import { Pressable, ScrollView, StyleSheet, View } from "react-native";
3
+ import { Pressable, StyleSheet, View } from "react-native";
4
4
  import { commonStyles } from "../constants/commonStyles";
5
5
  import { backgroundColor, radii, shadows } from "../constants/design";
6
6
  import { useBodyClassName } from "../hooks/useBodyClassName";
7
7
  import { FocusTrap } from "./FocusTrap";
8
8
  import { LoadingView } from "./LoadingView";
9
9
  import { Portal } from "./Portal";
10
+ import { ScrollView } from "./ScrollView";
10
11
  import { Suspendable } from "./Suspendable";
11
12
  import { TransitionView } from "./TransitionView";
12
13
  const BACKGROUND_COLOR = "rgba(0, 0, 0, 0.6)";
@@ -35,7 +35,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
35
35
  *
36
36
  */
37
37
  import { cloneElement, Fragment, memo, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from "react";
38
- import { ScrollView, StyleSheet, View, } from "react-native";
38
+ import { StyleSheet, View, } from "react-native";
39
39
  import { match } from "ts-pattern";
40
40
  import { v4 as uuid } from "uuid";
41
41
  import { backgroundColor, colors, invariantColors, radii, shadows, spacings, } from "../constants/design";
@@ -48,6 +48,7 @@ import { BorderedIcon } from "./BorderedIcon";
48
48
  import { Icon } from "./Icon";
49
49
  import { LakeHeading } from "./LakeHeading";
50
50
  import { LakeText } from "./LakeText";
51
+ import { ScrollView } from "./ScrollView";
51
52
  import { Space } from "./Space";
52
53
  const HORIZONTAL_SAFE_AREA = 10;
53
54
  export const SCROLLBAR_RESERVED_SPACE = 20;
@@ -1,6 +1,7 @@
1
1
  import { ForwardedRef, ReactNode } from "react";
2
- import { ScrollView, ScrollViewProps, StyleProp, ViewStyle, WebRole } from "react-native";
3
- export type FlatListRef = ScrollView;
2
+ import { StyleProp, ViewStyle, WebRole } from "react-native";
3
+ import { ScrollViewProps, ScrollViewRef } from "./ScrollView";
4
+ export type FlatListRef = ScrollViewRef;
4
5
  export type ListRenderItemInfo<T> = {
5
6
  item: T;
6
7
  index: number;
@@ -1,17 +1,20 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Fragment, forwardRef, useEffect, useRef } from "react";
3
- import { ScrollView, StyleSheet, View, } from "react-native";
3
+ import { StyleSheet, View } from "react-native";
4
+ import { ScrollView } from "./ScrollView";
4
5
  const styles = StyleSheet.create({
5
6
  scrollTracker: {
6
7
  position: "absolute",
7
8
  pointerEvents: "none",
8
- left: 0,
9
- bottom: 0,
10
9
  right: 0,
10
+ bottom: 0,
11
11
  },
12
12
  });
13
- const FlatListWithRef = ({ ItemSeparatorComponent, ListEmptyComponent, ListFooterComponent, ListHeaderComponent, contentContainerStyle, data, horizontal = false, keyExtractor, onEndReached, onEndReachedThresholdPx = 200, onKeyDown, onScroll, renderItem, role, scrollEventThrottle = 0, showsScrollIndicators = true, style, }, forwardedRef) => {
13
+ const FlatListWithRef = ({ ItemSeparatorComponent, ListEmptyComponent, ListFooterComponent, ListHeaderComponent, contentContainerStyle, data, horizontal = false, keyExtractor, onEndReached, onEndReachedThresholdPx = 200, onKeyDown, onScroll, renderItem, role, scrollEventThrottle = 16, showsScrollIndicators = true, style, }, forwardedRef) => {
14
14
  const scrollTrackerRef = useRef(null);
15
+ const scrollTrackerStyle = horizontal
16
+ ? { top: 0, width: onEndReachedThresholdPx }
17
+ : { left: 0, height: onEndReachedThresholdPx };
15
18
  useEffect(() => {
16
19
  const element = scrollTrackerRef.current;
17
20
  if (element != null) {
@@ -29,6 +32,8 @@ const FlatListWithRef = ({ ItemSeparatorComponent, ListEmptyComponent, ListFoote
29
32
  }
30
33
  // re-create an observer only on list length change
31
34
  }, [data.length]); // eslint-disable-line react-hooks/exhaustive-deps
32
- return (_jsxs(ScrollView, { contentContainerStyle: contentContainerStyle, horizontal: horizontal, onKeyDown: onKeyDown, onScroll: onScroll, ref: forwardedRef, role: role, scrollEventThrottle: scrollEventThrottle, showsHorizontalScrollIndicator: showsScrollIndicators, showsVerticalScrollIndicator: showsScrollIndicators, style: style, children: [ListHeaderComponent, data.length > 0 ? (_jsxs(View, { children: [data.map((item, index) => (_jsxs(Fragment, { children: [index !== 0 && ItemSeparatorComponent, renderItem({ item, index })] }, keyExtractor(item, index)))), _jsx(View, { ref: scrollTrackerRef, style: [styles.scrollTracker, { height: onEndReachedThresholdPx }] })] })) : (ListEmptyComponent), ListFooterComponent] }));
35
+ return (_jsxs(ScrollView, { contentContainerStyle: contentContainerStyle, horizontal: horizontal, onKeyDown: onKeyDown, onScroll: onScroll, ref: forwardedRef, role: role, scrollEventThrottle: scrollEventThrottle, showsScrollIndicators: showsScrollIndicators, style: style, children: [ListHeaderComponent, data.length <= 0
36
+ ? ListEmptyComponent
37
+ : data.map((item, index) => (_jsxs(Fragment, { children: [index !== 0 && ItemSeparatorComponent, renderItem({ item, index })] }, keyExtractor(item, index)))), ListFooterComponent, _jsx(View, { role: "none", ref: scrollTrackerRef, style: [styles.scrollTracker, scrollTrackerStyle] })] }));
33
38
  };
34
39
  export const FlatList = forwardRef(FlatListWithRef);
@@ -14,6 +14,7 @@ export declare const Form: import("react").MemoExoticComponent<import("react").F
14
14
  onKeyDownCapture?: ((event: NativeSyntheticEvent<import("react").KeyboardEvent<Element>>) => void) | undefined;
15
15
  onKeyUp?: ((event: NativeSyntheticEvent<import("react").KeyboardEvent<Element>>) => void) | undefined;
16
16
  onKeyUpCapture?: ((event: NativeSyntheticEvent<import("react").KeyboardEvent<Element>>) => void) | undefined;
17
+ onScroll?: ((event: NativeSyntheticEvent<import("react").UIEvent<Element, UIEvent>>) => void) | undefined;
17
18
  collapsable?: boolean | undefined;
18
19
  needsOffscreenAlphaCompositing?: boolean | undefined;
19
20
  renderToHardwareTextureAndroid?: boolean | undefined;
@@ -1,7 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useCallback, useState } from "react";
3
- import { ScrollView, StyleSheet, View, } from "react-native";
3
+ import { StyleSheet, View } from "react-native";
4
4
  import { backgroundColor } from "../constants/design";
5
+ import { ScrollView } from "./ScrollView";
5
6
  const HORIZONTAL_SAFE_AREA = 20;
6
7
  const styles = StyleSheet.create({
7
8
  root: {
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { cloneElement, Fragment, useCallback, useEffect, useId, useMemo, useRef, useState, } from "react";
3
- import { ScrollView, StyleSheet, View } from "react-native";
3
+ import { StyleSheet, View } from "react-native";
4
4
  import { commonStyles } from "../constants/commonStyles";
5
5
  import { backgroundColor, breakpoints, colors, spacings } from "../constants/design";
6
6
  import { useHover } from "../hooks/useHover";
@@ -8,6 +8,7 @@ import { isNotNullish, isNullish } from "../utils/nullish";
8
8
  import { PlainListViewPlaceholder, } from "./FixedListView";
9
9
  import { LakeHeading } from "./LakeHeading";
10
10
  import { ResponsiveContainer } from "./ResponsiveContainer";
11
+ import { ScrollView } from "./ScrollView";
11
12
  const INHERIT = "inherit";
12
13
  const styles = StyleSheet.create({
13
14
  root: {
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { memo, useCallback, useEffect, useRef, useState } from "react";
3
- import { Pressable, ScrollView, StyleSheet, View, } from "react-native";
3
+ import { Pressable, StyleSheet, View, } from "react-native";
4
4
  import { match, P } from "ts-pattern";
5
5
  import { animations, backgroundColor, radii, shadows, spacings, } from "../constants/design";
6
6
  import { useContextualLayer } from "../hooks/useContextualLayer";
@@ -9,6 +9,7 @@ import { noop } from "../utils/function";
9
9
  import { BottomPanel } from "./BottomPanel";
10
10
  import { FocusTrap } from "./FocusTrap";
11
11
  import { Portal } from "./Portal";
12
+ import { ScrollView } from "./ScrollView";
12
13
  import { TransitionView } from "./TransitionView";
13
14
  const styles = StyleSheet.create({
14
15
  container: {
@@ -0,0 +1,23 @@
1
+ /// <reference types="react" />
2
+ import { NativeScrollEvent, NativeSyntheticEvent, StyleProp, ViewProps, ViewStyle } from "react-native";
3
+ export type ScrollViewRef = {
4
+ scrollTo: (event: {
5
+ x?: number;
6
+ y?: number;
7
+ animated?: boolean;
8
+ }) => void;
9
+ };
10
+ export type ScrollViewProps = ViewProps & {
11
+ contentContainerStyle?: StyleProp<ViewStyle>;
12
+ horizontal?: boolean;
13
+ onScroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
14
+ scrollEventThrottle?: number;
15
+ showsScrollIndicators?: boolean;
16
+ };
17
+ export declare const ScrollView: import("react").ForwardRefExoticComponent<ViewProps & {
18
+ contentContainerStyle?: StyleProp<ViewStyle>;
19
+ horizontal?: boolean | undefined;
20
+ onScroll?: ((event: NativeSyntheticEvent<NativeScrollEvent>) => void) | undefined;
21
+ scrollEventThrottle?: number | undefined;
22
+ showsScrollIndicators?: boolean | undefined;
23
+ } & import("react").RefAttributes<ScrollViewRef>>;
@@ -0,0 +1,99 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef, useCallback, useImperativeHandle, useRef, } from "react";
3
+ import { StyleSheet, View, } from "react-native";
4
+ import { useMergeRefs } from "../hooks/useMergeRefs";
5
+ const styles = StyleSheet.create({
6
+ base: {
7
+ WebkitOverflowScrolling: "touch",
8
+ flexDirection: "column",
9
+ flexGrow: 1,
10
+ flexShrink: 1,
11
+ overflowX: "hidden",
12
+ overflowY: "auto",
13
+ transform: "translateZ(0)",
14
+ },
15
+ horizontal: {
16
+ flexDirection: "row",
17
+ overflowX: "auto",
18
+ overflowY: "hidden",
19
+ },
20
+ contentHorizontal: {
21
+ flexDirection: "row",
22
+ },
23
+ hideScrollbars: {
24
+ scrollbarWidth: "none",
25
+ },
26
+ });
27
+ const normalizeScrollEvent = (event) => {
28
+ const target = event.target;
29
+ const contentOffset = {
30
+ get x() {
31
+ return target.scrollLeft;
32
+ },
33
+ get y() {
34
+ return target.scrollTop;
35
+ },
36
+ };
37
+ const contentSize = {
38
+ get height() {
39
+ return target.scrollHeight;
40
+ },
41
+ get width() {
42
+ return target.scrollWidth;
43
+ },
44
+ };
45
+ const layoutMeasurement = {
46
+ get height() {
47
+ return target.offsetHeight;
48
+ },
49
+ get width() {
50
+ return target.offsetWidth;
51
+ },
52
+ };
53
+ return {
54
+ nativeEvent: { contentOffset, contentSize, layoutMeasurement },
55
+ timeStamp: Date.now(),
56
+ };
57
+ };
58
+ const shouldEmitScrollEvent = (state, eventThrottle) => !state.scrolling || (eventThrottle > 0 && Date.now() - state.lastTick >= eventThrottle);
59
+ export const ScrollView = forwardRef(({ children, contentContainerStyle, horizontal = false, onScroll, scrollEventThrottle = 16, showsScrollIndicators = true, style, ...viewProps }, forwardedRef) => {
60
+ const innerRef = useRef(null);
61
+ const stateRef = useRef({ lastTick: 0, scrolling: false });
62
+ const timeoutRef = useRef(null);
63
+ const mergedRef = useMergeRefs(innerRef, forwardedRef);
64
+ const handleOnScroll = useCallback((event) => {
65
+ event.stopPropagation();
66
+ // A scroll happened, so the scroll resets the scrollend timeout.
67
+ if (timeoutRef.current != null) {
68
+ clearTimeout(timeoutRef.current);
69
+ }
70
+ timeoutRef.current = setTimeout(() => {
71
+ stateRef.current.scrolling = false;
72
+ onScroll === null || onScroll === void 0 ? void 0 : onScroll(normalizeScrollEvent(event));
73
+ }, 100);
74
+ if (shouldEmitScrollEvent(stateRef.current, scrollEventThrottle)) {
75
+ stateRef.current.scrolling = true;
76
+ stateRef.current.lastTick = Date.now();
77
+ onScroll === null || onScroll === void 0 ? void 0 : onScroll(normalizeScrollEvent(event));
78
+ }
79
+ }, [onScroll, scrollEventThrottle]);
80
+ const scrollTo = useCallback(({ x = 0, y = 0, animated = true }) => {
81
+ const element = innerRef.current;
82
+ if (element != null) {
83
+ if (typeof element.scroll === "function") {
84
+ element.scroll({ top: y, left: x, behavior: !animated ? "auto" : "smooth" });
85
+ }
86
+ else {
87
+ element.scrollTop = y;
88
+ element.scrollLeft = x;
89
+ }
90
+ }
91
+ }, []);
92
+ useImperativeHandle(innerRef, () => ({ scrollTo }));
93
+ return (_jsx(View, { ...viewProps, ref: mergedRef, onScroll: handleOnScroll, style: [
94
+ styles.base,
95
+ style,
96
+ horizontal && styles.horizontal,
97
+ !showsScrollIndicators && styles.hideScrollbars,
98
+ ], children: _jsx(View, { style: [horizontal && styles.contentHorizontal, contentContainerStyle], children: children }) }));
99
+ });
@@ -1,7 +1,8 @@
1
1
  import { ForwardedRef, ReactNode } from "react";
2
- import { ScrollView, ScrollViewProps, StyleProp, ViewStyle, WebRole } from "react-native";
2
+ import { StyleProp, ViewStyle, WebRole } from "react-native";
3
3
  import { ListRenderItemInfo } from "./FlatList";
4
- export type SectionListRef = ScrollView;
4
+ import { ScrollViewProps, ScrollViewRef } from "./ScrollView";
5
+ export type SectionListRef = ScrollViewRef;
5
6
  type Section<T> = {
6
7
  title: string;
7
8
  data: T[];
@@ -1,18 +1,21 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Fragment, forwardRef, useEffect, useId, useRef } from "react";
3
- import { ScrollView, StyleSheet, View, } from "react-native";
3
+ import { StyleSheet, View } from "react-native";
4
+ import { ScrollView } from "./ScrollView";
4
5
  const styles = StyleSheet.create({
5
6
  scrollTracker: {
6
7
  position: "absolute",
7
8
  pointerEvents: "none",
8
- bottom: 0,
9
- left: 0,
10
9
  right: 0,
10
+ bottom: 0,
11
11
  },
12
12
  });
13
- const SectionListWithRef = ({ ItemSeparatorComponent, ListEmptyComponent, ListFooterComponent, ListHeaderComponent, contentContainerStyle, horizontal = false, keyExtractor, onEndReached, onEndReachedThresholdPx = 200, onKeyDown, onScroll, renderItem, renderSectionHeader, role, scrollEventThrottle = 0, sections, showsScrollIndicators = true, style, }, forwardedRef) => {
13
+ const SectionListWithRef = ({ ItemSeparatorComponent, ListEmptyComponent, ListFooterComponent, ListHeaderComponent, contentContainerStyle, horizontal = false, keyExtractor, onEndReached, onEndReachedThresholdPx = 200, onKeyDown, onScroll, renderItem, renderSectionHeader, role, scrollEventThrottle = 16, sections, showsScrollIndicators = true, style, }, forwardedRef) => {
14
14
  const groupId = useId();
15
15
  const scrollTrackerRef = useRef(null);
16
+ const scrollTrackerStyle = horizontal
17
+ ? { top: 0, width: onEndReachedThresholdPx }
18
+ : { left: 0, height: onEndReachedThresholdPx };
16
19
  useEffect(() => {
17
20
  const element = scrollTrackerRef.current;
18
21
  if (element != null) {
@@ -30,6 +33,8 @@ const SectionListWithRef = ({ ItemSeparatorComponent, ListEmptyComponent, ListFo
30
33
  }
31
34
  // re-create an observer only on list length change
32
35
  }, [sections.length]); // eslint-disable-line react-hooks/exhaustive-deps
33
- return (_jsxs(ScrollView, { contentContainerStyle: contentContainerStyle, horizontal: horizontal, onKeyDown: onKeyDown, onScroll: onScroll, ref: forwardedRef, role: role, scrollEventThrottle: scrollEventThrottle, showsHorizontalScrollIndicator: showsScrollIndicators, showsVerticalScrollIndicator: showsScrollIndicators, style: style, children: [ListHeaderComponent, sections.length > 0 ? (_jsxs(View, { children: [sections.map(section => (_jsxs(Fragment, { children: [renderSectionHeader === null || renderSectionHeader === void 0 ? void 0 : renderSectionHeader(section), section.data.map((item, index) => (_jsxs(Fragment, { children: [index !== 0 && ItemSeparatorComponent, renderItem({ item, index })] }, keyExtractor(item, index))))] }, `group-${groupId}-${section.title}`))), _jsx(View, { ref: scrollTrackerRef, style: [styles.scrollTracker, { height: onEndReachedThresholdPx }] })] })) : (ListEmptyComponent), ListFooterComponent] }));
36
+ return (_jsxs(ScrollView, { contentContainerStyle: contentContainerStyle, horizontal: horizontal, onKeyDown: onKeyDown, onScroll: onScroll, ref: forwardedRef, role: role, scrollEventThrottle: scrollEventThrottle, showsScrollIndicators: showsScrollIndicators, style: style, children: [ListHeaderComponent, sections.length <= 0
37
+ ? ListEmptyComponent
38
+ : sections.map(section => (_jsxs(Fragment, { children: [renderSectionHeader === null || renderSectionHeader === void 0 ? void 0 : renderSectionHeader(section), section.data.map((item, index) => (_jsxs(Fragment, { children: [index !== 0 && ItemSeparatorComponent, renderItem({ item, index })] }, keyExtractor(item, index))))] }, `group-${groupId}-${section.title}`))), ListFooterComponent, _jsx(View, { role: "none", ref: scrollTrackerRef, style: [styles.scrollTracker, scrollTrackerStyle] })] }));
34
39
  };
35
40
  export const SectionList = forwardRef(SectionListWithRef);
@@ -1,4 +1,4 @@
1
- import { ScrollViewProps } from "react-native";
1
+ import { ScrollViewProps } from "./ScrollView";
2
2
  type Props = {
3
3
  style?: ScrollViewProps["style"];
4
4
  contentContainerStyle?: ScrollViewProps["style"];
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { createContext, useCallback, useContext, useEffect, useRef, useState } from "react";
3
- import { ScrollView, StyleSheet, View } from "react-native";
3
+ import { StyleSheet, View } from "react-native";
4
+ import { ScrollView } from "./ScrollView";
4
5
  const styles = StyleSheet.create({
5
6
  track: {
6
7
  position: "absolute",