@swan-io/lake 4.5.1 → 4.6.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": "4.5.1",
3
+ "version": "4.6.0",
4
4
  "engines": {
5
5
  "node": ">=18.0.0",
6
6
  "yarn": "^1.22.0"
@@ -1,15 +1,12 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { memo } from "react";
3
- import { StyleSheet, Text, View } from "react-native";
3
+ import { StyleSheet, View } from "react-native";
4
4
  import { commonStyles } from "../constants/commonStyles";
5
5
  import { colors } from "../constants/design";
6
- import { typography } from "../constants/typography";
7
6
  import { Icon } from "./Icon";
7
+ import { LakeText } from "./LakeText";
8
8
  const styles = StyleSheet.create({
9
9
  text: {
10
- ...typography.bodyLarge,
11
- fontWeight: typography.fontWeights.demi,
12
- textAlign: "center",
13
10
  userSelect: "none",
14
11
  },
15
12
  container: {
@@ -39,5 +36,5 @@ export const Avatar = memo(({ initials = "", size }) => {
39
36
  width: size,
40
37
  borderRadius: size / 2,
41
38
  },
42
- ], children: initials !== "" ? (_jsx(Text, { style: [styles.text, { color: colors[variant][500], fontSize: size * 0.4 }], children: initials })) : (_jsx(Icon, { name: "person-filled", size: size - 8, color: colors[variant][50] })) }));
39
+ ], children: initials !== "" ? (_jsx(LakeText, { variant: "semibold", align: "center", style: [styles.text, { color: colors[variant][500], fontSize: size * 0.4 }], children: initials })) : (_jsx(Icon, { name: "person-filled", size: size - 8, color: colors[variant][50] })) }));
43
40
  });
@@ -1,12 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useRef } from "react";
3
- import { FlatList, Pressable, StyleSheet, Text, View } from "react-native";
3
+ import { FlatList, Pressable, StyleSheet, View } from "react-native";
4
4
  import { colors } from "../constants/design";
5
- import { typography } from "../constants/typography";
6
5
  import { useDisclosure } from "../hooks/useDisclosure";
7
6
  import { Box } from "./Box";
8
7
  import { Icon } from "./Icon";
9
8
  import { LakeButton } from "./LakeButton";
9
+ import { LakeText } from "./LakeText";
10
10
  import { Popover } from "./Popover";
11
11
  import { Space } from "./Space";
12
12
  const styles = StyleSheet.create({
@@ -31,22 +31,17 @@ const styles = StyleSheet.create({
31
31
  backgroundColor: colors.gray[50],
32
32
  },
33
33
  availableFiltersTitle: {
34
- ...typography.bodyLarge,
35
- color: colors.current[500],
36
34
  paddingHorizontal: 24,
37
35
  },
38
- filterName: {
39
- ...typography.bodySmall,
40
- },
41
36
  });
42
37
  export function FilterChooser({ filters, openFilters, label, title, availableFilters, large = true, onAddFilter, }) {
43
38
  const inputRef = useRef(null);
44
39
  const [visible, { close, toggle }] = useDisclosure(false);
45
- return (_jsxs(_Fragment, { children: [_jsx(Box, { direction: "row", justifyContent: "start", alignItems: "center", children: _jsx(LakeButton, { size: "small", mode: "secondary", color: "gray", onPress: toggle, ref: inputRef, icon: large ? "chevron-down-filled" : "filter-filled", iconPosition: "end", ariaLabel: label, children: large ? label : null }) }), _jsx(Popover, { role: "listbox", matchReferenceMinWidth: true, onDismiss: close, referenceRef: inputRef, returnFocus: false, visible: visible, children: _jsxs(View, { style: styles.list, children: [_jsx(Text, { style: styles.availableFiltersTitle, children: title }), _jsx(Space, { height: 8 }), _jsx(FlatList, { role: "list", data: availableFilters, keyExtractor: (_, index) => `filter-item-${index}`, renderItem: ({ item }) => {
40
+ return (_jsxs(_Fragment, { children: [_jsx(Box, { direction: "row", justifyContent: "start", alignItems: "center", children: _jsx(LakeButton, { size: "small", mode: "secondary", color: "gray", onPress: toggle, ref: inputRef, icon: large ? "chevron-down-filled" : "filter-filled", iconPosition: "end", ariaLabel: label, children: large ? label : null }) }), _jsx(Popover, { role: "listbox", matchReferenceMinWidth: true, onDismiss: close, referenceRef: inputRef, returnFocus: false, visible: visible, children: _jsxs(View, { style: styles.list, children: [_jsx(LakeText, { style: styles.availableFiltersTitle, children: title }), _jsx(Space, { height: 8 }), _jsx(FlatList, { role: "list", data: availableFilters, keyExtractor: (_, index) => `filter-item-${index}`, renderItem: ({ item }) => {
46
41
  const isSet = Boolean(filters[item.name]) || openFilters.includes(item.name);
47
42
  return (_jsxs(Pressable, { style: ({ hovered }) => [styles.item, hovered && styles.itemHovered], role: "button", disabled: isSet, onPress: () => {
48
43
  onAddFilter(item.name);
49
44
  close();
50
- }, children: [_jsx(Text, { style: [styles.filterName, isSet && styles.selected], children: item.label }), isSet && _jsx(Icon, { color: colors.positive[500], name: "checkmark-filled", size: 14 })] }));
45
+ }, children: [_jsx(LakeText, { variant: "smallRegular", style: isSet && styles.selected, children: item.label }), isSet && _jsx(Icon, { color: colors.positive[500], name: "checkmark-filled", size: 14 })] }));
51
46
  } })] }) })] }));
52
47
  }
@@ -275,12 +275,19 @@ const styles = StyleSheet.create({
275
275
  borderBottomRightRadius: radii[4],
276
276
  },
277
277
  emptyListContainer: {
278
- flexGrow: 1,
278
+ position: "absolute",
279
+ top: 0,
280
+ left: 0,
281
+ right: 0,
282
+ bottom: 0,
283
+ backgroundColor: backgroundColor.default,
284
+ },
285
+ emptyListContentContainer: {
279
286
  flexDirection: "column",
280
287
  alignItems: "center",
281
288
  justifyContent: "center",
282
289
  padding: spacings[48],
283
- backgroundColor: backgroundColor.default,
290
+ minHeight: "100%",
284
291
  },
285
292
  emptyList: {
286
293
  flexDirection: "column",
@@ -721,7 +728,7 @@ export const FixedListView = ({ data: originalData, mode = "tile", keyExtractor,
721
728
  setHasHorizontalScroll(centerColumnsWidth > width);
722
729
  }, [centerColumnsWidth]);
723
730
  const isLoading = isNotNullish(loading) && loading.isLoading;
724
- return (_jsxs(View, { style: styles.root, children: [_jsx(View, { ref: startFocusAnchorRef, tabIndex: 0 }), data.length === 0 && isNotNullish(renderEmptyList) && !isLoading ? (_jsx(View, { style: styles.emptyListContainer, children: renderEmptyList() })) : (_jsxs(ScrollView, { onKeyDown: onKeyDown, onLayout: onLayout, onScroll: onScroll, scrollEventThrottle: 32, style: [styles.container, mode === "tile" && styles.containerTile], contentContainerStyle: [
731
+ 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: [
725
732
  styles.contentContainer,
726
733
  {
727
734
  height: totalHeight +
@@ -804,7 +811,7 @@ export const FixedListView = ({ data: originalData, mode = "tile", keyExtractor,
804
811
  ], children: [_jsx(View, { style: [
805
812
  styles.stickyColumnEndOverflow,
806
813
  { width: horizontalPadding, backgroundColor: headerBackgroundColor },
807
- ] }), _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] })] })), _jsx(View, { ref: endFocusAnchorRef, tabIndex: 0 })] }));
814
+ ] }), _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 })] }));
808
815
  };
809
816
  export const FixedListViewPlaceholder = ({ count, rowHeight, rowVerticalSpacing, groupHeaderHeight, headerHeight, paddingHorizontal = HORIZONTAL_SAFE_AREA, }) => {
810
817
  const totalRowHeight = rowHeight + rowVerticalSpacing;
@@ -1,16 +1,15 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { StyleSheet, Text } from "react-native";
3
- import { colors } from "../constants/colors";
4
- import { typography } from "../constants/typography";
2
+ import { StyleSheet } from "react-native";
3
+ import { colors, texts } from "../constants/design";
4
+ import { LakeText } from "./LakeText";
5
5
  import { Space } from "./Space";
6
6
  const styles = StyleSheet.create({
7
7
  base: {
8
- ...typography.bodySmall,
9
- color: colors.red[100],
10
- lineHeight: typography.lineHeights.input,
8
+ color: colors.negative[500],
9
+ lineHeight: texts.h1.lineHeight,
11
10
  minHeight: 32,
12
11
  paddingHorizontal: 3, // borderRadius / 2
13
12
  paddingVertical: 4,
14
13
  },
15
14
  });
16
- export const InputError = ({ message = "", style }) => message !== "" ? _jsx(Text, { style: [styles.base, style], children: message }) : _jsx(Space, { height: 32 });
15
+ export const InputError = ({ message = "", style }) => message !== "" ? (_jsx(LakeText, { variant: "smallRegular", style: [styles.base, style], children: message })) : (_jsx(Space, { height: 32 }));
@@ -2,12 +2,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { forwardRef, useCallback, useId, useImperativeHandle, useRef, useState, } from "react";
3
3
  import { FlatList, Pressable, StyleSheet, Text, View, } from "react-native";
4
4
  import { backgroundColor, colors, spacings } from "../constants/design";
5
- import { typography } from "../constants/typography";
6
5
  import { useMergeRefs } from "../hooks/useMergeRefs";
7
6
  import { getFocusableElements } from "../utils/a11y";
8
7
  import { isNotEmpty } from "../utils/nullish";
9
8
  import { Box } from "./Box";
10
9
  import { Icon } from "./Icon";
10
+ import { LakeText } from "./LakeText";
11
11
  import { LakeTextInput } from "./LakeTextInput";
12
12
  import { LoadingView } from "./LoadingView";
13
13
  import { Popover } from "./Popover";
@@ -41,7 +41,6 @@ const styles = StyleSheet.create({
41
41
  backgroundColor: colors.gray[100],
42
42
  },
43
43
  itemText: {
44
- ...typography.bodyLarge,
45
44
  userSelect: "none",
46
45
  },
47
46
  loader: {
@@ -171,7 +170,7 @@ const LakeComboboxWithRef = ({ inputRef, value, items, itemHeight = DEFAULT_ELEM
171
170
  setIsFetchingAdditionalInfo(false);
172
171
  dismiss();
173
172
  });
174
- }, children: isReactText(rendered) ? (_jsx(Text, { numberOfLines: 1, style: styles.itemText, children: rendered })) : (rendered) }));
173
+ }, children: isReactText(rendered) ? (_jsx(LakeText, { numberOfLines: 1, style: styles.itemText, children: rendered })) : (rendered) }));
175
174
  } })), ListFooterComponent, isFetchingAdditionalInfo ? (_jsxs(View, { style: styles.loaderAdditional, children: [_jsx(View, { style: styles.loaderAdditionalUnderlay }), _jsx(LoadingView, {})] })) : null] })),
176
175
  }),
177
176
  }) }) })] }));
@@ -7,6 +7,7 @@ export type LakeTagInputProps = Merge<TextInputProps, {
7
7
  disabled?: boolean;
8
8
  valid?: boolean;
9
9
  hideErrors?: boolean;
10
+ validateOnBlur?: boolean;
10
11
  help?: string;
11
12
  validator?: (value: string) => boolean;
12
13
  values: string[];
@@ -211,6 +212,7 @@ export declare const LakeTagInput: import("react").ForwardRefExoticComponent<{
211
212
  disabled?: boolean | undefined;
212
213
  valid?: boolean | undefined;
213
214
  hideErrors?: boolean | undefined;
215
+ validateOnBlur?: boolean | undefined;
214
216
  help?: string | undefined;
215
217
  validator?: ((value: string) => boolean) | undefined;
216
218
  placeholder?: string | undefined;
@@ -77,7 +77,7 @@ const styles = StyleSheet.create({
77
77
  },
78
78
  });
79
79
  const SEPARATORS_REGEX = /,| /;
80
- export const LakeTagInput = forwardRef(({ validator = () => true, onFocus: originalOnFocus, onBlur: originalOnBlur, values, onValuesChanged, readOnly = false, disabled = false, valid = false, hideErrors = false, placeholder, help, error, }, forwardRef) => {
80
+ export const LakeTagInput = forwardRef(({ validator = () => true, onFocus: originalOnFocus, onBlur: originalOnBlur, validateOnBlur = true, values, onValuesChanged, readOnly = false, disabled = false, valid = false, hideErrors = false, placeholder, help, error, }, forwardRef) => {
81
81
  const inputRef = useRef(null);
82
82
  const containerRef = useRef(null);
83
83
  const [isFocused, setIsFocused] = useState(false);
@@ -97,6 +97,9 @@ export const LakeTagInput = forwardRef(({ validator = () => true, onFocus: origi
97
97
  }
98
98
  }, [pushNewValues]);
99
99
  const onTextInputKeyPress = useCallback(({ nativeEvent }) => {
100
+ if (disabled || readOnly) {
101
+ return;
102
+ }
100
103
  match({ key: nativeEvent.key, input: inputRef.current })
101
104
  .with({ key: "Backspace", input: P.instanceOf(HTMLInputElement) }, ({ input }) => {
102
105
  if (isNullishOrEmpty(input.value)) {
@@ -108,7 +111,7 @@ export const LakeTagInput = forwardRef(({ validator = () => true, onFocus: origi
108
111
  pushNewValues([input.value]);
109
112
  }
110
113
  });
111
- }, [onValuesChanged, pushNewValues, values]);
114
+ }, [onValuesChanged, pushNewValues, values, disabled, readOnly]);
112
115
  const autoFocus = useCallback(() => {
113
116
  inputRef.current?.focus();
114
117
  }, []);
@@ -117,9 +120,15 @@ export const LakeTagInput = forwardRef(({ validator = () => true, onFocus: origi
117
120
  originalOnFocus?.(event);
118
121
  }, [originalOnFocus]);
119
122
  const onBlur = useCallback((event) => {
123
+ const input = inputRef.current;
124
+ if (input instanceof HTMLInputElement &&
125
+ isNotNullishOrEmpty(input.value) &&
126
+ validateOnBlur) {
127
+ pushNewValues([input.value]);
128
+ }
120
129
  setIsFocused(false);
121
130
  originalOnBlur?.(event);
122
- }, [originalOnBlur]);
131
+ }, [pushNewValues, originalOnBlur, validateOnBlur]);
123
132
  const mergedRef = useMergeRefs(inputRef, forwardRef);
124
133
  const hasError = isNotNullishOrEmpty(error);
125
134
  return (_jsxs(View, { children: [_jsxs(Pressable, { style: [
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { forwardRef } from "react";
3
3
  import { ActivityIndicator, StyleSheet, View } from "react-native";
4
- import { colors } from "../constants/colors";
4
+ import { colors } from "../constants/design";
5
5
  const styles = StyleSheet.create({
6
6
  base: {
7
7
  alignItems: "center",
@@ -21,4 +21,4 @@ const styles = StyleSheet.create({
21
21
  },
22
22
  });
23
23
  const isDev = process.env.NODE_ENV === "development";
24
- export const LoadingView = forwardRef(({ color = colors.gray[100], delay = isDev ? 0 : 1000, style }, forwardedRef) => (_jsx(View, { ref: forwardedRef, style: [styles.base, style], children: _jsx(ActivityIndicator, { size: "small", color: color, style: [styles.indicator, delay > 0 && { animationDelay: delay.toString() + "ms" }] }) })));
24
+ export const LoadingView = forwardRef(({ color = colors.gray[400], delay = isDev ? 0 : 1000, style }, forwardedRef) => (_jsx(View, { ref: forwardedRef, style: [styles.base, style], children: _jsx(ActivityIndicator, { size: "small", color: color, style: [styles.indicator, delay > 0 && { animationDelay: delay.toString() + "ms" }] }) })));
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { StyleSheet } from "react-native";
3
- import { colors } from "../constants/colors";
3
+ import { invariantColors } from "../constants/design";
4
4
  import { Path, Svg } from "./Svg";
5
5
  const styles = StyleSheet.create({
6
6
  base: {
@@ -8,4 +8,4 @@ const styles = StyleSheet.create({
8
8
  width: 45,
9
9
  },
10
10
  });
11
- export const SwanLogo = ({ color = colors.gray[100], style }) => (_jsxs(Svg, { viewBox: "0 0 45 10", style: [styles.base, style], children: [_jsx("title", { children: "Swan" }), _jsx(Path, { d: "M41.01 0c2.2 0 3.66 1.6 3.66 3.82v5.95H42.9V3.9c0-1.28-1-2.19-2.23-2.19a2.33 2.33 0 00-2.37 2.4v5.65h-1.83V.27h1.83v.92A3.4 3.4 0 0141 0zM30.77 9.73H29.2c-2.65 0-4.53-2.27-4.53-4.86A4.81 4.81 0 0129.44 0c2.92 0 4.93 1.74 4.93 4.45v5.32h-1.71V4.45c0-1.72-1.2-2.76-3.22-2.76a3.11 3.11 0 00-3.06 3.18c0 1.72 1.2 3.17 2.82 3.17h1.57v1.7zm-14.37-7l-2.25 7.04h-2.1L9.07.27h1.96l2.11 7.16L15.45.27h1.9l2.3 7.16L21.76.27h1.96l-3 9.5h-2.1zM4.14 10C1.94 10 .54 8.82.36 6.93L.33 6.7h1.79l.02.18c.15.98.84 1.48 2.07 1.48s1.93-.54 1.93-1.38c0-.82-.5-1.1-2.15-1.42l-.43-.09C1.54 5.05.64 4.41.64 2.76.64 1.1 2.1 0 4.18 0 6.06 0 7.5 1.12 7.65 2.83l.02.23H5.9l-.02-.19c-.12-.79-.72-1.25-1.79-1.25-1.03 0-1.68.47-1.68 1.14 0 .68.47.92 1.99 1.2l.6.12c2 .4 2.93 1.1 2.93 2.83C7.93 8.76 6.4 10 4.14 10z", fill: color })] }));
11
+ export const SwanLogo = ({ color = invariantColors.gray, style }) => (_jsxs(Svg, { viewBox: "0 0 45 10", style: [styles.base, style], children: [_jsx("title", { children: "Swan" }), _jsx(Path, { d: "M41.01 0c2.2 0 3.66 1.6 3.66 3.82v5.95H42.9V3.9c0-1.28-1-2.19-2.23-2.19a2.33 2.33 0 00-2.37 2.4v5.65h-1.83V.27h1.83v.92A3.4 3.4 0 0141 0zM30.77 9.73H29.2c-2.65 0-4.53-2.27-4.53-4.86A4.81 4.81 0 0129.44 0c2.92 0 4.93 1.74 4.93 4.45v5.32h-1.71V4.45c0-1.72-1.2-2.76-3.22-2.76a3.11 3.11 0 00-3.06 3.18c0 1.72 1.2 3.17 2.82 3.17h1.57v1.7zm-14.37-7l-2.25 7.04h-2.1L9.07.27h1.96l2.11 7.16L15.45.27h1.9l2.3 7.16L21.76.27h1.96l-3 9.5h-2.1zM4.14 10C1.94 10 .54 8.82.36 6.93L.33 6.7h1.79l.02.18c.15.98.84 1.48 2.07 1.48s1.93-.54 1.93-1.38c0-.82-.5-1.1-2.15-1.42l-.43-.09C1.54 5.05.64 4.41.64 2.76.64 1.1 2.1 0 4.18 0 6.06 0 7.5 1.12 7.65 2.83l.02.23H5.9l-.02-.19c-.12-.79-.72-1.25-1.79-1.25-1.03 0-1.68.47-1.68 1.14 0 .68.47.92 1.99 1.2l.6.12c2 .4 2.93 1.1 2.93 2.83C7.93 8.76 6.4 10 4.14 10z", fill: color })] }));
@@ -36,6 +36,7 @@ export declare const invariantColors: {
36
36
  white: string;
37
37
  transparent: string;
38
38
  gray: string;
39
+ defaultAccentColor: string;
39
40
  };
40
41
  export type ColorVariants = keyof typeof colors;
41
42
  export type BackgroundColorVariant = {
@@ -230,6 +230,7 @@ export const invariantColors = {
230
230
  white: "#fff",
231
231
  transparent: "transparent",
232
232
  gray: "#16141a",
233
+ defaultAccentColor: "#6240b5",
233
234
  };
234
235
  export const backgroundColor = {
235
236
  default: "var(--color-background-default)",
@@ -1,3 +1,3 @@
1
1
  import { AsyncData, Future, Result } from "@swan-io/boxed";
2
- import { AnyVariables, CombinedError, TypedDocumentNode } from "urql";
3
- export declare const useUrqlMutation: <Data, Variables extends AnyVariables>(query: TypedDocumentNode<Data, Variables>) => readonly [AsyncData<Result<Data, Error>>, (input: Variables) => Future<Result<Data, Error | CombinedError>>];
2
+ import { AnyVariables, CombinedError, OperationContext, TypedDocumentNode } from "urql";
3
+ export declare const useUrqlMutation: <Data, Variables extends AnyVariables>(query: TypedDocumentNode<Data, Variables>) => readonly [AsyncData<Result<Data, Error>>, (input: Variables, context?: Partial<OperationContext>) => Future<Result<Data, Error | CombinedError>>];
@@ -1,6 +1,6 @@
1
1
  import { AsyncData, Future, Result } from "@swan-io/boxed";
2
2
  import { useCallback, useMemo } from "react";
3
- import { CombinedError, useMutation } from "urql";
3
+ import { CombinedError, useMutation, } from "urql";
4
4
  import { isNotNullish, isNullish } from "../utils/nullish";
5
5
  const toResult = ({ data, error, }) => {
6
6
  if (isNotNullish(error)) {
@@ -23,7 +23,7 @@ export const useUrqlMutation = (query) => {
23
23
  }
24
24
  return AsyncData.Done(toResult({ data, error }));
25
25
  }, [fetching, data, error]),
26
- useCallback((input) => Future.fromPromise(execute(input))
26
+ useCallback((input, context) => Future.fromPromise(execute(input, context))
27
27
  .mapError(error => error) // Only used to cast error
28
28
  .mapOkToResult(toResult), [execute]),
29
29
  ];