@swan-io/lake 1.0.0 → 1.2.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": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "engines": {
5
5
  "node": ">=14.0.0",
6
6
  "yarn": "^1.20.0"
@@ -9,8 +9,7 @@
9
9
  "LICENSE",
10
10
  "src/**/*.js",
11
11
  "src/**/*.d.ts",
12
- "README.md",
13
- "HISTORY.md"
12
+ "README.md"
14
13
  ],
15
14
  "publishConfig": {
16
15
  "access": "public",
@@ -23,6 +22,7 @@
23
22
  "not ie <= 11",
24
23
  "safari >= 12"
25
24
  ],
25
+ "license": "MIT",
26
26
  "dependencies": {
27
27
  "@popperjs/core": "2.11.6",
28
28
  "@swan-io/boxed": "0.12.1",
@@ -32,7 +32,6 @@
32
32
  "polished": "4.2.2",
33
33
  "prism-react-renderer": "1.3.5",
34
34
  "react": "18.2.0",
35
- "react-atomic-state": "1.2.7",
36
35
  "react-dom": "18.2.0",
37
36
  "react-dropzone": "14.2.3",
38
37
  "react-fast-compare": "3.2.0",
@@ -40,6 +39,7 @@
40
39
  "react-popper": "2.3.0",
41
40
  "react-ux-form": "1.3.0",
42
41
  "rifm": "0.12.1",
42
+ "ts-dedent": "2.2.0",
43
43
  "ts-pattern": "4.2.1",
44
44
  "urql": "3.0.3",
45
45
  "uuid": "9.0.0"
@@ -53,7 +53,6 @@
53
53
  "@types/react-native": "0.70.11",
54
54
  "@types/uuid": "9.0.1",
55
55
  "jsdom": "21.1.0",
56
- "ts-dedent": "2.2.0",
57
56
  "type-fest": "3.6.1",
58
57
  "vitest": "0.29.2"
59
58
  }
@@ -1,8 +1,6 @@
1
1
  /// <reference types="react" />
2
2
  export declare function FilterChooser<FilterName extends string>({ filters, openFilters, label, title, availableFilters, large, onAddFilter, }: {
3
- filters: Partial<{
4
- [K in FilterName]: string | string[] | undefined;
5
- }>;
3
+ filters: Partial<Record<FilterName, unknown>>;
6
4
  openFilters: FilterName[];
7
5
  label: string;
8
6
  title: string;
@@ -36,12 +36,18 @@ export type FilterInputDef = {
36
36
  placeholder?: string;
37
37
  validate?: (value: string) => ValidatorResult;
38
38
  };
39
- type Filter<T> = FilterCheckboxDef<T> | FilterRadioDef<T> | FilterDateDef | FilterInputDef;
39
+ export type FilterBooleanDef = {
40
+ type: "boolean";
41
+ label: string;
42
+ };
43
+ type Filter<T> = FilterCheckboxDef<T> | FilterRadioDef<T> | FilterDateDef | FilterInputDef | FilterBooleanDef;
40
44
  type ExtractFilterValue<T extends Filter<unknown>> = T extends {
41
45
  type: "checkbox";
42
46
  } ? T["items"][number]["value"][] | undefined : T extends {
43
47
  type: "radio";
44
- } ? T["items"][number]["value"] | undefined : string | undefined;
48
+ } ? T["items"][number]["value"] | undefined : T extends {
49
+ type: "boolean";
50
+ } ? boolean | undefined : string | undefined;
45
51
  type FiltersDefinition = Record<string, Filter<unknown>>;
46
52
  export type FiltersState<T extends FiltersDefinition> = Simplify<{
47
53
  [K in keyof T]: Simplify<ExtractFilterValue<T[K]>>;
@@ -224,6 +224,10 @@ export const FiltersStack = ({ filters, openedFilters, definition, onChangeOpene
224
224
  onChangeFilters({ ...filters, [filterName]: undefined });
225
225
  onChangeOpened(openedFilters.filter(f => f !== filterName));
226
226
  } })))
227
+ .with({ type: "boolean" }, ({ label }) => (_jsx(Tag, { color: "current", onPressRemove: () => {
228
+ onChangeFilters({ ...filters, [filterName]: undefined });
229
+ onChangeOpened(openedFilters.filter(f => f !== filterName));
230
+ }, children: label })))
227
231
  .exhaustive() }, filterName));
228
232
  }) }));
229
233
  };
@@ -1,8 +1,8 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { StyleSheet, Text } from "react-native";
3
+ import { Space } from "./Space";
3
4
  import { colors } from "../constants/colors";
4
5
  import { typography } from "../constants/typography";
5
- import { Space } from "./Space";
6
6
  const styles = StyleSheet.create({
7
7
  base: {
8
8
  ...typography.bodySmall,
@@ -2,6 +2,7 @@ import { AsyncData, Result } from "@swan-io/boxed";
2
2
  import { ForwardedRef, ReactNode, RefObject } from "react";
3
3
  import { IconName } from "./Icon";
4
4
  export type LakeComboboxProps<I> = {
5
+ inputRef?: RefObject<unknown>;
5
6
  value: string;
6
7
  items: AsyncData<Result<I[], unknown>>;
7
8
  ListFooterComponent?: ReactNode;
@@ -21,7 +22,7 @@ export type LakeComboboxRef = {
21
22
  close: () => void;
22
23
  open: () => void;
23
24
  };
24
- declare const LakeComboboxWithRef: <I>({ value, items, ListFooterComponent, onValueChange, onSelectItem, renderItem, keyExtractor, icon, placeholder, disabled, emptyResultText, readOnly, nativeID, error, }: LakeComboboxProps<I>, externalRef: ForwardedRef<LakeComboboxRef>) => JSX.Element;
25
+ declare const LakeComboboxWithRef: <I>({ inputRef, value, items, ListFooterComponent, onValueChange, onSelectItem, renderItem, keyExtractor, icon, placeholder, disabled, emptyResultText, readOnly, nativeID, error, }: LakeComboboxProps<I>, externalRef: ForwardedRef<LakeComboboxRef>) => JSX.Element;
25
26
  export declare const LakeCombobox: <I>(props: LakeComboboxProps<I> & {
26
27
  ref?: RefObject<LakeComboboxRef> | undefined;
27
28
  }) => ReturnType<typeof LakeComboboxWithRef>;
@@ -4,6 +4,7 @@ import { FlatList, Pressable, StyleSheet, Text, View, } from "react-native";
4
4
  import { backgroundColor, colors, spacings } from "../constants/design";
5
5
  import { typography } from "../constants/typography";
6
6
  import { useDisclosure } from "../hooks/useDisclosure";
7
+ import { useMergeRefs } from "../hooks/useMergeRefs";
7
8
  import { getFocusableElements } from "../utils/a11y";
8
9
  import { Box } from "./Box";
9
10
  import { Icon } from "./Icon";
@@ -80,8 +81,10 @@ const getItemLayout = (_data, index) => ({
80
81
  offset: ELEMENT_HEIGHT * index,
81
82
  index,
82
83
  });
83
- const LakeComboboxWithRef = ({ value, items, ListFooterComponent, onValueChange, onSelectItem, renderItem, keyExtractor, icon, placeholder, disabled = false, emptyResultText, readOnly, nativeID, error, }, externalRef) => {
84
+ const LakeComboboxWithRef = ({ inputRef, value, items, ListFooterComponent, onValueChange, onSelectItem, renderItem, keyExtractor, icon, placeholder, disabled = false, emptyResultText, readOnly, nativeID, error, }, externalRef) => {
84
85
  const ref = useRef(null);
86
+ // @ts-expect-error
87
+ const inputTextRef = useMergeRefs(ref, inputRef);
85
88
  const listRef = useRef(null);
86
89
  const listContainerRef = useRef(null);
87
90
  const blurTimeoutId = useRef(undefined);
@@ -140,7 +143,11 @@ const LakeComboboxWithRef = ({ value, items, ListFooterComponent, onValueChange,
140
143
  close();
141
144
  }, 100);
142
145
  }, [close]);
143
- return (_jsxs(View, { children: [_jsx(LakeTextInput, { ref: ref, style: styles.input, accessibilityExpanded: isFocused, accessibilityControls: isFocused ? suggestionsId : "", returnKeyType: "search", icon: icon, accessibilityRole: "combobox", placeholder: placeholder, value: value, disabled: disabled, error: error, onChangeText: onValueChange, onFocus: handleFocus, onBlur: handleBlur, onKeyPress: handleKeyPress, nativeID: nativeID, readOnly: readOnly }), _jsx(Popover, { id: suggestionsId, role: "listbox", matchReferenceWidth: true, onDismiss: close, referenceRef: ref, autoFocus: true, returnFocus: false, visible: isFocused && !items.isNotAsked(), underlay: false, forcedMode: "Dropdown", children: _jsx(View, { style: styles.list, children: items.match({
146
+ return (_jsxs(View, { children: [_jsx(LakeTextInput
147
+ // @ts-expect-error
148
+ , {
149
+ // @ts-expect-error
150
+ ref: inputTextRef, style: styles.input, accessibilityExpanded: isFocused, accessibilityControls: isFocused ? suggestionsId : "", returnKeyType: "search", icon: icon, accessibilityRole: "combobox", placeholder: placeholder, value: value, disabled: disabled, error: error, onChangeText: onValueChange, onFocus: handleFocus, onBlur: handleBlur, onKeyPress: handleKeyPress, nativeID: nativeID, readOnly: readOnly }), _jsx(Popover, { id: suggestionsId, role: "listbox", matchReferenceWidth: true, onDismiss: close, referenceRef: ref, autoFocus: true, returnFocus: false, visible: isFocused && !items.isNotAsked(), underlay: false, forcedMode: "Dropdown", children: _jsx(View, { style: styles.list, children: items.match({
144
151
  NotAsked: () => null,
145
152
  Loading: () => _jsx(LoadingView, { style: styles.loader }),
146
153
  Done: items => items.match({
@@ -153,6 +160,7 @@ const LakeComboboxWithRef = ({ value, items, ListFooterComponent, onValueChange,
153
160
  focused && styles.focusedItem,
154
161
  pressed && styles.pressedItem,
155
162
  ], onPress: () => {
163
+ window.clearTimeout(blurTimeoutId.current);
156
164
  setIsFetchingAdditionalInfo(true);
157
165
  Promise.resolve(onSelectItem(item)).finally(() => {
158
166
  setIsFetchingAdditionalInfo(false);
@@ -1,12 +1,13 @@
1
1
  import { ReactNode } from "react";
2
2
  import { StyleProp, ViewStyle } from "react-native";
3
3
  import { ColorVariants } from "../constants/design";
4
+ type LabelType = "form" | "formSmall" | "view" | "viewSmall" | "radioGroup";
4
5
  type Props = {
5
6
  label: string;
6
7
  optionalLabel?: string;
7
8
  readOnlyColor?: string;
8
9
  color?: ColorVariants;
9
- type?: "form" | "formSmall" | "view" | "viewSmall";
10
+ type?: LabelType;
10
11
  extra?: () => ReactNode;
11
12
  help?: ReactNode;
12
13
  render: (id: string) => ReactNode;
@@ -14,6 +15,6 @@ type Props = {
14
15
  readOnly?: boolean;
15
16
  style?: StyleProp<ViewStyle>;
16
17
  };
17
- export declare const defaultLabelType: NonNullable<Props["type"]>;
18
+ export declare const defaultLabelType: LabelType;
18
19
  export declare const LakeLabel: ({ label, optionalLabel, extra, readOnly, color, readOnlyColor, type, help, render, actions, style, }: Props) => JSX.Element;
19
20
  export {};
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useCallback, useRef, useState } from "react";
3
3
  import { StyleSheet, unstable_createElement, View, } from "react-native";
4
+ import { match } from "ts-pattern";
4
5
  import { v4 as uuid } from "uuid";
5
6
  import { commonStyles } from "../constants/commonStyles";
6
7
  import { colors, fonts, spacings, texts } from "../constants/design";
@@ -39,5 +40,9 @@ export const LakeLabel = ({ label, optionalLabel, extra, readOnly = false, color
39
40
  target?.focus();
40
41
  }
41
42
  }, [id]);
42
- return (_jsxs(Box, { style: [styles.container, style], direction: "row", alignItems: "center", justifyContent: "spaceBetween", children: [_jsxs(View, { style: commonStyles.fill, ref: containerRef, children: [_jsxs(Box, { direction: "row", justifyContent: "spaceBetween", alignItems: "center", children: [_jsxs(Box, { direction: "row", style: styles.shrink, children: [type === "form" || type === "formSmall" ? (_jsxs(Label, { onClick: onClick, htmlFor: id, style: [styles.label, readOnly && { color: readOnlyColor }], children: [label, optionalLabel != null ? (_jsxs(_Fragment, { children: [" - ", _jsx(LakeText, { color: colors.gray[400], style: styles.optionalLabel, children: optionalLabel })] })) : null] })) : (_jsxs(LakeText, { variant: "medium", color: readOnlyColor, nativeID: id, children: [label, optionalLabel != null ? (_jsxs(_Fragment, { children: [" - ", _jsx(LakeText, { color: colors.gray[400], style: styles.optionalLabel, children: optionalLabel })] })) : null] })), isNotNullish(extra) && extra()] }), isNotNullish(help) && (_jsxs(_Fragment, { children: [_jsx(Space, { width: 16 }), help] }))] }), _jsx(Space, { height: type === "formSmall" || type === "viewSmall" ? 4 : 8 }), _jsx(View, { accessibilityLabelledBy: type === "view" || type === "viewSmall" ? id : undefined, children: render(id) })] }), isNotNullish(actions) && (_jsxs(_Fragment, { children: [_jsx(Space, { width: 16 }), actions] }))] }));
43
+ return (_jsxs(Box, { style: [styles.container, style], direction: "row", alignItems: "center", justifyContent: "spaceBetween", children: [_jsxs(View, { style: commonStyles.fill, ref: containerRef, children: [_jsxs(Box, { direction: "row", justifyContent: "spaceBetween", alignItems: "center", children: [_jsxs(Box, { direction: "row", style: styles.shrink, children: [type === "form" || type === "formSmall" || type === "radioGroup" ? (_jsxs(Label, { onClick: onClick, htmlFor: id, style: [styles.label, readOnly && { color: readOnlyColor }], children: [label, optionalLabel != null ? (_jsxs(_Fragment, { children: [" - ", _jsx(LakeText, { color: colors.gray[400], style: styles.optionalLabel, children: optionalLabel })] })) : null] })) : (_jsxs(LakeText, { variant: "medium", color: readOnlyColor, nativeID: id, children: [label, optionalLabel != null ? (_jsxs(_Fragment, { children: [" - ", _jsx(LakeText, { color: colors.gray[400], style: styles.optionalLabel, children: optionalLabel })] })) : null] })), isNotNullish(extra) && extra()] }), isNotNullish(help) && (_jsxs(_Fragment, { children: [_jsx(Space, { width: 16 }), help] }))] }), _jsx(Space, { height: match(type)
44
+ .with("formSmall", "viewSmall", () => 4)
45
+ .with("form", "view", () => 8)
46
+ .with("radioGroup", () => 12)
47
+ .exhaustive() }), _jsx(View, { accessibilityLabelledBy: type === "view" || type === "viewSmall" ? id : undefined, children: render(id) })] }), isNotNullish(actions) && (_jsxs(_Fragment, { children: [_jsx(Space, { width: 16 }), actions] }))] }));
43
48
  };
@@ -128,5 +128,5 @@ export const LakeModal = ({ visible, icon, title, color = "current", children, m
128
128
  if (rootElement == null) {
129
129
  return null;
130
130
  }
131
- return (_jsx(Portal, { container: rootElement, children: _jsxs(View, { accessibilityModal: true, style: styles.container, pointerEvents: visible ? "auto" : "none", children: [_jsx(TransitionView, { style: styles.fill, enter: styles.overlayEnter, leave: styles.overlayLeave, children: visible ? _jsx(View, { style: styles.overlay }) : null }), _jsx(Suspense, { fallback: _jsx(LoadingView, { color: backgroundColor.accented, delay: 0 }), children: _jsx(TransitionView, { style: styles.fill, enter: styles.modalEnter, leave: styles.modalLeave, children: visible ? (_jsx(ResponsiveContainer, { style: styles.root, breakpoint: breakpoints.tiny, children: ({ large, small }) => (_jsx(FocusTrap, { autoFocus: true, focusLock: true, returnFocus: true, onEscapeKey: onPressClose, style: styles.trap, children: _jsxs(ScrollView, { style: styles.modalContainer, contentContainerStyle: large ? styles.modalContentContainer : styles.modalContentContainerSmall, children: [onPressClose != null ? (_jsx(Pressable, { onPress: onPressClose, style: styles.pressableOverlay })) : null, _jsxs(View, { style: [large ? styles.modal : styles.modalSmall, { maxWidth }], children: [_jsxs(View, { style: styles.modalHeader, children: [_jsxs(View, { style: styles.modalIconTitle, children: [icon != null ? (_jsx(Icon, { name: icon, size: large ? 40 : 32, color: colors[color].primary })) : null, icon != null && title != null ? _jsx(Space, { height: 12 }) : null, title != null ? (_jsxs(_Fragment, { children: [_jsx(LakeHeading, { level: 2, variant: "h3", children: title }), _jsx(Space, { height: 12 })] })) : null, icon != null && title != null ? _jsx(Space, { height: 8 }) : null] }), onPressClose != null ? (_jsx(LakeButton, { style: styles.closeButton, mode: "tertiary", onPress: onPressClose, icon: "lake-close" })) : null] }), typeof children == "function" ? children({ large, small }) : children] })] }) })) })) : null }) })] }) }));
131
+ return (_jsx(Portal, { container: rootElement, children: _jsxs(View, { accessibilityModal: true, style: styles.container, pointerEvents: visible ? "auto" : "none", children: [_jsx(TransitionView, { style: styles.fill, enter: styles.overlayEnter, leave: styles.overlayLeave, children: visible ? _jsx(View, { style: styles.overlay }) : null }), _jsx(Suspense, { fallback: _jsx(LoadingView, { color: backgroundColor.accented, delay: 0 }), children: _jsx(TransitionView, { style: styles.fill, enter: styles.modalEnter, leave: styles.modalLeave, children: visible ? (_jsx(ResponsiveContainer, { style: styles.root, breakpoint: breakpoints.tiny, children: ({ large, small }) => (_jsx(FocusTrap, { autoFocus: true, focusLock: true, returnFocus: true, onEscapeKey: onPressClose, style: styles.trap, children: _jsxs(ScrollView, { style: styles.modalContainer, contentContainerStyle: large ? styles.modalContentContainer : styles.modalContentContainerSmall, children: [onPressClose != null ? (_jsx(Pressable, { onPress: onPressClose, style: styles.pressableOverlay })) : null, _jsxs(View, { style: [large ? styles.modal : styles.modalSmall, { maxWidth }], children: [_jsxs(View, { style: styles.modalHeader, children: [_jsxs(View, { style: styles.modalIconTitle, children: [icon != null ? (_jsx(Icon, { name: icon, size: large ? 40 : 32, color: colors[color][500] })) : null, icon != null && title != null ? _jsx(Space, { height: 12 }) : null, title != null ? (_jsxs(_Fragment, { children: [_jsx(LakeHeading, { level: 2, variant: "h3", children: title }), _jsx(Space, { height: 12 })] })) : null] }), onPressClose != null ? (_jsx(LakeButton, { style: styles.closeButton, mode: "tertiary", onPress: onPressClose, icon: "lake-close" })) : null] }), typeof children == "function" ? children({ large, small }) : children] })] }) })) })) : null }) })] }) }));
132
132
  };
@@ -13,6 +13,9 @@ const linkStyle = {
13
13
  textDecoration: "none",
14
14
  };
15
15
  const styles = StyleSheet.create({
16
+ container: {
17
+ alignItems: "flex-end",
18
+ },
16
19
  bar: {
17
20
  width: "100%",
18
21
  height: 4,
@@ -70,7 +73,7 @@ export const LakeStepper = ({ steps, activeStepId, style }) => {
70
73
  .exhaustive())
71
74
  .flat();
72
75
  const activeStepIndex = stepIds.indexOf(activeStepId);
73
- return (_jsx(Grid, { numColumns: steps.length, horizontalSpace: 12, style: style, children: steps.map((step, index) => {
76
+ return (_jsx(Grid, { numColumns: steps.length, horizontalSpace: 12, style: [styles.container, style], children: steps.map((step, index) => {
74
77
  const stepNumber = index + 1;
75
78
  const currentId = match(step)
76
79
  .with({ id: P.string }, ({ id }) => id)
@@ -5,6 +5,7 @@ import { IconName } from "./Icon";
5
5
  export type LakeTextInputProps = Omit<TextInputProps, "editable" | "onChange"> & {
6
6
  error?: string;
7
7
  readOnly?: boolean;
8
+ validating?: boolean;
8
9
  valid?: boolean;
9
10
  disabled?: boolean;
10
11
  color?: ColorVariants;
@@ -21,6 +22,7 @@ export type LakeTextInputProps = Omit<TextInputProps, "editable" | "onChange"> &
21
22
  export declare const LakeTextInput: import("react").ForwardRefExoticComponent<Omit<TextInputProps, "editable" | "onChange"> & {
22
23
  error?: string | undefined;
23
24
  readOnly?: boolean | undefined;
25
+ validating?: boolean | undefined;
24
26
  valid?: boolean | undefined;
25
27
  disabled?: boolean | undefined;
26
28
  color?: "warning" | "current" | "gray" | "live" | "sandbox" | "positive" | "negative" | "partner" | "swan" | "shakespear" | "darkPink" | "sunglow" | "mediumSladeBlue" | undefined;
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { forwardRef, useCallback, useRef, useState } from "react";
3
- import { StyleSheet, TextInput, View, } from "react-native";
3
+ import { ActivityIndicator, StyleSheet, TextInput, View, } from "react-native";
4
4
  import { commonStyles } from "../constants/commonStyles";
5
5
  import { backgroundColor, colors, radii, shadows, spacings, texts, } from "../constants/design";
6
6
  import { useHover } from "../hooks/useHover";
@@ -116,7 +116,7 @@ const styles = StyleSheet.create({
116
116
  boxShadow: shadows.tile,
117
117
  },
118
118
  });
119
- export const LakeTextInput = forwardRef(({ error, disabled = false, valid = false, readOnly = false, icon, children, unit, color = "gray", keyboardType = "default", hideErrors = false, onChange, pattern, style: stylesFromProps, onFocus: originalOnFocus, onBlur: originalOnBlur, value, defaultValue, multiline = false, ...props }, forwardRef) => {
119
+ export const LakeTextInput = forwardRef(({ error, disabled = false, validating = false, valid = false, readOnly = false, icon, children, unit, color = "gray", keyboardType = "default", hideErrors = false, onChange, pattern, style: stylesFromProps, onFocus: originalOnFocus, onBlur: originalOnBlur, value, defaultValue, multiline = false, ...props }, forwardRef) => {
120
120
  const inputRef = useRef(null);
121
121
  const [isHovered, setIsHovered] = useState(false);
122
122
  const [isFocused, setIsFocused] = useState(false);
@@ -150,5 +150,5 @@ export const LakeTextInput = forwardRef(({ error, disabled = false, valid = fals
150
150
  isFocused && styles.focused,
151
151
  isFocused && { borderColor: colors[color][500] },
152
152
  stylesFromProps,
153
- ] }), hasError && (_jsx(Icon, { name: "warning-regular", size: 20, color: colors.negative[400], style: [styles.endIcon, readOnly && styles.readOnlyEndIcon] })), !hasError && valid && (_jsx(Icon, { name: "checkmark-filled", size: 20, color: colors.positive[400], style: [styles.endIcon, readOnly && styles.readOnlyEndIcon] })), isNotNullish(icon) && (_jsx(Icon, { name: icon, size: 20, color: colors.current.primary, style: styles.icon }))] }), isNotNullish(unit) && (_jsx(LakeText, { color: colors.gray[900], style: [styles.unit, (disabled || readOnly) && styles.unitDisabled], children: unit }))] }), children] }), !hideErrors && (_jsx(LakeText, { color: colors.negative[400], style: styles.errorText, children: error ?? " " }))] }));
153
+ ] }), validating && (_jsx(ActivityIndicator, { size: "small", style: styles.endIcon, color: colors.current[500] })), !validating && hasError && (_jsx(Icon, { name: "warning-regular", size: 20, color: colors.negative[400], style: [styles.endIcon, readOnly && styles.readOnlyEndIcon] })), !validating && !hasError && valid && (_jsx(Icon, { name: "checkmark-filled", size: 20, color: colors.positive[400], style: [styles.endIcon, readOnly && styles.readOnlyEndIcon] })), isNotNullish(icon) && (_jsx(Icon, { name: icon, size: 20, color: colors.current.primary, style: styles.icon }))] }), isNotNullish(unit) && (_jsx(LakeText, { color: colors.gray[900], style: [styles.unit, (disabled || readOnly) && styles.unitDisabled], children: unit }))] }), children] }), !hideErrors && (_jsx(LakeText, { color: colors.negative[400], style: styles.errorText, children: error ?? " " }))] }));
154
154
  });
@@ -194,7 +194,7 @@ export const PlainListView = ({ data: originalData, keyExtractor, rowHeight, gro
194
194
  ], children: _jsx(LakeHeading, { level: 3, variant: "h3", children: groupName }) })) : null, items.map((item, index) => {
195
195
  const key = keyExtractor(item, index);
196
196
  const isActive = activeRowId === key;
197
- const isHovered = hoveredRow === key;
197
+ const isHovered = isNotNullish(getRowLink) && hoveredRow === key;
198
198
  const wrapper = createRowWrapper({ item, absoluteIndex: index, extraInfo });
199
199
  return cloneElement(wrapper, {
200
200
  style: { ...styles.rowLink, ...wrapper.props.style },
@@ -16,10 +16,15 @@ const styles = StyleSheet.create({
16
16
  flexDirection: "row",
17
17
  alignItems: "center",
18
18
  },
19
- rowItem: {
19
+ withRightSpace: {
20
20
  // use margin instead of <Space /> to avoid line starting with <Space /> because of flex-wrap
21
21
  marginRight: 24,
22
22
  },
23
+ // We need this bottom margin in case there are too much items and some of them are wrapped to next line
24
+ withBottomSpace: {
25
+ // use margin instead of <Space /> to avoid making height bigger than expected
26
+ marginBottom: 12,
27
+ },
23
28
  });
24
29
  export function RadioGroup({ items, direction = "column", color = "current", disabled = false, value, onValueChange, }) {
25
30
  return (_jsx(Box, { direction: direction, style: styles.container, children: items.map((item, index) => {
@@ -29,6 +34,10 @@ export function RadioGroup({ items, direction = "column", color = "current", dis
29
34
  if (item.value !== value) {
30
35
  onValueChange(item.value);
31
36
  }
32
- }, style: [styles.item, direction === "row" && !isLast && styles.rowItem], children: [_jsx(LakeRadio, { disabled: isDisabled, color: color, value: item.value === value }), _jsx(Space, { width: 12 }), _jsx(LakeText, { color: isDisabled ? colors.gray[300] : colors.gray[900], children: item.name })] }), !isLast && _jsx(Space, { height: direction === "column" ? 12 : 32 })] }, index));
37
+ }, style: [
38
+ styles.item,
39
+ direction === "row" && styles.withBottomSpace,
40
+ direction === "row" && !isLast && styles.withRightSpace,
41
+ ], children: [_jsx(LakeRadio, { disabled: isDisabled, color: color, value: item.value === value }), _jsx(Space, { width: 12 }), _jsx(LakeText, { color: isDisabled ? colors.gray[300] : colors.gray[900], children: item.name })] }), !isLast && _jsx(Space, { height: direction === "column" ? 12 : undefined })] }, index));
33
42
  }) }));
34
43
  }
@@ -1,9 +1,9 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { forwardRef, memo, useEffect } from "react";
3
3
  import { Animated, Pressable, StyleSheet, View } from "react-native";
4
+ import { Icon } from "./Icon";
4
5
  import { backgroundColor, colors, shadows } from "../constants/design";
5
6
  import { useAnimatedValue } from "../hooks/useAnimatedValue";
6
- import { Icon } from "./Icon";
7
7
  const WIDTH = 36;
8
8
  const BUTTON_SIZE = 16;
9
9
  const PADDING = 2;
@@ -1,5 +1,6 @@
1
1
  /// <reference types="react" />
2
2
  import { IconName } from "@swan-io/lake/src/components/Icon";
3
+ import { SpacingValue } from "@swan-io/lake/src/components/Space";
3
4
  type Tab = {
4
5
  label: string;
5
6
  url: string;
@@ -11,6 +12,7 @@ type Props = {
11
12
  tabs: Tab[];
12
13
  otherLabel: string;
13
14
  hideIfSingleItem?: boolean;
15
+ padding?: SpacingValue;
14
16
  };
15
- export declare const TabView: ({ tabs, otherLabel, hideIfSingleItem }: Props) => JSX.Element | null;
17
+ export declare const TabView: ({ tabs, otherLabel, hideIfSingleItem, padding }: Props) => JSX.Element | null;
16
18
  export {};
@@ -249,7 +249,7 @@ const DropdownItems = forwardRef(({ tabs, otherLabel, currentUrl }, ref) => {
249
249
  isNotNullish(activeTab) ? styles.activeLink : hovered ? styles.hoveredLink : null,
250
250
  ], children: [_jsx(Text, { children: otherLabel }), _jsx(Space, { width: 8 }), _jsx(Text, { style: styles.count, children: tabs.length }), _jsx(Space, { width: 4 }), _jsx(Icon, { name: "chevron-down-filled", size: 12 })] }), _jsx(TransitionView, { ...animations.fadeAndSlideInFromBottom, style: styles.dropdownPlacement, children: shouldOpen ? (_jsx(FocusTrap, { autoFocus: shouldAutoFocus, focusLock: shouldLockFocus, returnFocus: shouldLockFocus, onClickOutside: onPressOutside, onEscapeKey: shouldLockFocus ? onEscapeKey : undefined, children: _jsx(Dropdown, { tabs: tabs, onHoverStart: onHoverStart, onHoverEnd: onHoverEnd, onLinkFocus: onLinkFocus, onLinkBlur: onAnyBlur, onLinkPress: onEscapeKey }) })) : null })] }));
251
251
  });
252
- export const TabView = ({ tabs, otherLabel, hideIfSingleItem = true }) => {
252
+ export const TabView = ({ tabs, otherLabel, hideIfSingleItem = true, padding }) => {
253
253
  const containerRef = useRef(null);
254
254
  const placeholderRef = useRef(null);
255
255
  const otherPlaceholderRef = useRef(null);
@@ -268,14 +268,15 @@ export const TabView = ({ tabs, otherLabel, hideIfSingleItem = true }) => {
268
268
  for (const [link, node] of values) {
269
269
  if ("/" + path.join("/") === link && isNotNullish(node) && isNotNullish(container)) {
270
270
  node.measureLayout(container, (left, _, width) => {
271
- setUnderlinePosition({ left, width });
271
+ const leftOffset = padding ?? 0;
272
+ setUnderlinePosition({ left: left - leftOffset, width });
272
273
  }, noop);
273
274
  return;
274
275
  }
275
276
  }
276
277
  }
277
278
  setUnderlinePosition({ left: 0, width: 0 });
278
- }, [path, kept, collapsed]);
279
+ }, [path, kept, collapsed, padding]);
279
280
  useEffect(() => {
280
281
  setHasRendered(width > 0);
281
282
  }, [width]);
@@ -370,7 +371,7 @@ export const TabView = ({ tabs, otherLabel, hideIfSingleItem = true }) => {
370
371
  if (tabs.length <= 1 && hideIfSingleItem) {
371
372
  return null;
372
373
  }
373
- return (_jsxs(Box, { alignItems: "center", direction: "row", accessibilityRole: "tablist", ref: containerRef, style: styles.container, children: [_jsxs(View, { style: styles.placeholder, accessibilityHidden: true, ref: placeholderRef, pointerEvents: "none", onLayout: onLayout, children: [tabs.map(({ label, url, icon, count }) => (_jsxs(Fragment, { children: [_jsxs(Link, { ref: ref => {
374
+ return (_jsxs(Box, { alignItems: "center", direction: "row", accessibilityRole: "tablist", ref: containerRef, style: [styles.container, { paddingHorizontal: padding }], children: [_jsxs(View, { style: styles.placeholder, accessibilityHidden: true, ref: placeholderRef, pointerEvents: "none", onLayout: onLayout, children: [tabs.map(({ label, url, icon, count }) => (_jsxs(Fragment, { children: [_jsxs(Link, { ref: ref => {
374
375
  if (placeholderLinkRef.current) {
375
376
  placeholderLinkRef.current[url] = ref;
376
377
  }
@@ -25,6 +25,10 @@ export declare const commonStyles: {
25
25
  readonly flexGrow: 1;
26
26
  readonly flexShrink: 1;
27
27
  };
28
+ readonly fillNoShrink: {
29
+ readonly flexGrow: 1;
30
+ readonly flexShrink: 0;
31
+ };
28
32
  readonly hidden: {
29
33
  readonly visibility: "hidden";
30
34
  };
@@ -39,6 +39,7 @@ export const commonStyles = {
39
39
  center: { alignItems: "center", justifyContent: "center" },
40
40
  centerSelf: { marginHorizontal: "auto" },
41
41
  fill: { flexGrow: 1, flexShrink: 1 },
42
+ fillNoShrink: { flexGrow: 1, flexShrink: 0 },
42
43
  hidden: { visibility: "hidden" },
43
44
  view: viewStyle,
44
45
  visuallyHidden: visuallyHiddenStyle,
@@ -1,6 +1,6 @@
1
1
  import { ComponentProps } from "react";
2
2
  import { Rifm } from "rifm";
3
- export type Props = Required<Pick<ComponentProps<typeof Rifm>, "accept" | "append" | "format" | "mask">>;
3
+ export type RifmProps = Required<Pick<ComponentProps<typeof Rifm>, "accept" | "append" | "format" | "mask">>;
4
4
  declare const accepted: {
5
5
  alpha: RegExp;
6
6
  numeric: RegExp;
@@ -10,5 +10,5 @@ export declare const getRifmProps: ({ accept, charMap, maxLength, }: {
10
10
  accept: keyof typeof accepted;
11
11
  maxLength: number;
12
12
  charMap: Record<number, string>;
13
- }) => Props;
13
+ }) => RifmProps;
14
14
  export {};
package/HISTORY.md DELETED
@@ -1,3 +0,0 @@
1
- # 1.0.0
2
-
3
- Initial release!