@swan-io/lake 1.4.4 → 1.5.1

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.
Files changed (108) hide show
  1. package/package.json +4 -5
  2. package/src/components/AppOpeningAnimation.js +13 -13
  3. package/src/components/AutoWidthImage.d.ts +1 -0
  4. package/src/components/AutoWidthImage.js +3 -4
  5. package/src/components/Avatar.js +3 -2
  6. package/src/components/BorderedButton.d.ts +1 -1
  7. package/src/components/BorderedButton.js +6 -4
  8. package/src/components/BottomPanel.js +2 -2
  9. package/src/components/Box.js +1 -1
  10. package/src/components/Breadcrumbs.js +7 -7
  11. package/src/components/ChoicePicker.js +6 -6
  12. package/src/components/Fill.js +2 -1
  13. package/src/components/FilterChooser.js +2 -2
  14. package/src/components/Filters.js +9 -9
  15. package/src/components/FixedListView.js +15 -12
  16. package/src/components/FixedListViewCells.js +2 -2
  17. package/src/components/Form.d.ts +1 -1
  18. package/src/components/FullViewportLayer.js +10 -7
  19. package/src/components/Heading.d.ts +1 -1
  20. package/src/components/Heading.js +1 -1
  21. package/src/components/Icon.d.ts +2 -4
  22. package/src/components/Icon.js +7 -1
  23. package/src/components/Input.d.ts +6 -6
  24. package/src/components/Input.js +6 -11
  25. package/src/components/Label.d.ts +1 -1
  26. package/src/components/Label.js +1 -1
  27. package/src/components/LakeButton.d.ts +3 -3
  28. package/src/components/LakeButton.js +5 -5
  29. package/src/components/LakeCheckbox.js +2 -2
  30. package/src/components/LakeCombobox.d.ts +2 -2
  31. package/src/components/LakeCombobox.js +6 -10
  32. package/src/components/LakeDownloadButton.js +1 -1
  33. package/src/components/LakeHeading.d.ts +3 -2
  34. package/src/components/LakeHeading.js +7 -1
  35. package/src/components/LakeLabel.d.ts +0 -1
  36. package/src/components/LakeLabel.js +3 -3
  37. package/src/components/LakeModal.js +6 -3
  38. package/src/components/LakeRadio.js +3 -3
  39. package/src/components/LakeScrollView.js +2 -1
  40. package/src/components/LakeSearchField.js +3 -3
  41. package/src/components/LakeSelect.d.ts +2 -2
  42. package/src/components/LakeSelect.js +14 -23
  43. package/src/components/LakeSlider.js +1 -1
  44. package/src/components/LakeStepper.js +4 -4
  45. package/src/components/LakeText.d.ts +9 -8
  46. package/src/components/LakeText.js +7 -1
  47. package/src/components/LakeTextInput.d.ts +10 -5
  48. package/src/components/LakeTextInput.js +4 -4
  49. package/src/components/LakeTooltip.js +7 -12
  50. package/src/components/Link.d.ts +52 -49
  51. package/src/components/Link.js +2 -2
  52. package/src/components/ListRightPanel.js +2 -2
  53. package/src/components/Modal.js +15 -5
  54. package/src/components/MultiSelect.d.ts +1 -1
  55. package/src/components/MultiSelect.js +10 -7
  56. package/src/components/PlainListView.js +4 -3
  57. package/src/components/Popover.js +9 -5
  58. package/src/components/Portal.js +2 -2
  59. package/src/components/Pressable.d.ts +112 -101
  60. package/src/components/Pressable.js +16 -6
  61. package/src/components/RightPanel.js +10 -7
  62. package/src/components/Separator.js +1 -1
  63. package/src/components/SidebarNavigationTracker.js +5 -4
  64. package/src/components/Slider.js +28 -12
  65. package/src/components/SmsOpeningAnimation.js +15 -15
  66. package/src/components/Stack.d.ts +4 -4
  67. package/src/components/Stack.js +1 -1
  68. package/src/components/Stepper.js +1 -1
  69. package/src/components/Svg.d.ts +6 -7
  70. package/src/components/Switch.js +18 -6
  71. package/src/components/TabView.js +8 -7
  72. package/src/components/Tag.d.ts +2 -2
  73. package/src/components/Tag.js +6 -3
  74. package/src/components/Tile.js +2 -2
  75. package/src/components/ToastStack.js +16 -7
  76. package/src/components/Tooltip.js +3 -6
  77. package/src/components/TransitionGroupView.d.ts +2 -3
  78. package/src/components/TransitionGroupView.js +2 -2
  79. package/src/components/TransitionView.d.ts +2 -3
  80. package/src/components/TransitionView.js +2 -2
  81. package/src/components/WithCurrentColor.d.ts +1 -1
  82. package/src/constants/design.d.ts +4 -12
  83. package/src/constants/design.js +12 -12
  84. package/src/hooks/useAnimatedValue.js +9 -2
  85. package/src/hooks/useForceableState.js +1 -1
  86. package/src/hooks/useHover.js +1 -1
  87. package/src/hooks/useMergeRefs.js +1 -1
  88. package/src/hooks/usePressEvents.js +1 -1
  89. package/src/components/Alert.d.ts +0 -10
  90. package/src/components/Alert.js +0 -36
  91. package/src/components/Button.d.ts +0 -15
  92. package/src/components/Button.js +0 -83
  93. package/src/components/Checkbox.d.ts +0 -12
  94. package/src/components/Checkbox.js +0 -83
  95. package/src/components/Combobox.d.ts +0 -29
  96. package/src/components/Combobox.js +0 -182
  97. package/src/components/MultilineInput.d.ts +0 -15
  98. package/src/components/MultilineInput.js +0 -55
  99. package/src/components/Picker.d.ts +0 -26
  100. package/src/components/Picker.js +0 -116
  101. package/src/components/ProgressBar.d.ts +0 -11
  102. package/src/components/ProgressBar.js +0 -46
  103. package/src/components/SegmentedControl.d.ts +0 -19
  104. package/src/components/SegmentedControl.js +0 -74
  105. package/src/components/Table.d.ts +0 -34
  106. package/src/components/Table.js +0 -79
  107. package/src/hooks/useLazyRef.d.ts +0 -2
  108. package/src/hooks/useLazyRef.js +0 -9
@@ -135,24 +135,16 @@ export declare const animations: {
135
135
  enter: {
136
136
  animationKeyframes: {
137
137
  "10%, 90%": {
138
- transform: {
139
- translateX: number;
140
- }[];
138
+ transform: string;
141
139
  };
142
140
  "20%, 80%": {
143
- transform: {
144
- translateX: number;
145
- }[];
141
+ transform: string;
146
142
  };
147
143
  "30%, 50%, 70%": {
148
- transform: {
149
- translateX: number;
150
- }[];
144
+ transform: string;
151
145
  };
152
146
  "40%, 60%": {
153
- transform: {
154
- translateX: number;
155
- }[];
147
+ transform: string;
156
148
  };
157
149
  }[];
158
150
  animationDuration: string;
@@ -439,7 +439,7 @@ const fadeAndSlideInFromTop = StyleSheet.create({
439
439
  animationKeyframes: {
440
440
  from: {
441
441
  opacity: 0,
442
- transform: [{ translateY: -10 }],
442
+ transform: "translateY(-10px)",
443
443
  },
444
444
  },
445
445
  animationDuration: "300ms",
@@ -449,7 +449,7 @@ const fadeAndSlideInFromTop = StyleSheet.create({
449
449
  animationKeyframes: {
450
450
  to: {
451
451
  opacity: 0,
452
- transform: [{ translateY: -10 }],
452
+ transform: "translateY(-10px)",
453
453
  },
454
454
  },
455
455
  animationDuration: "300ms",
@@ -462,7 +462,7 @@ const fadeAndSlideInFromLeft = StyleSheet.create({
462
462
  animationKeyframes: {
463
463
  from: {
464
464
  opacity: 0,
465
- transform: [{ translateX: -10 }],
465
+ transform: "translateX(-10px)",
466
466
  },
467
467
  },
468
468
  animationDuration: "300ms",
@@ -472,7 +472,7 @@ const fadeAndSlideInFromLeft = StyleSheet.create({
472
472
  animationKeyframes: {
473
473
  to: {
474
474
  opacity: 0,
475
- transform: [{ translateX: -10 }],
475
+ transform: "translateX(-10px)",
476
476
  },
477
477
  },
478
478
  animationDuration: "300ms",
@@ -485,7 +485,7 @@ const fadeAndSlideInFromBottom = StyleSheet.create({
485
485
  animationKeyframes: {
486
486
  from: {
487
487
  opacity: 0,
488
- transform: [{ translateY: 10 }],
488
+ transform: "translateY(10px)",
489
489
  },
490
490
  },
491
491
  animationDuration: "300ms",
@@ -495,7 +495,7 @@ const fadeAndSlideInFromBottom = StyleSheet.create({
495
495
  animationKeyframes: {
496
496
  to: {
497
497
  opacity: 0,
498
- transform: [{ translateY: 10 }],
498
+ transform: "translateY(10px)",
499
499
  },
500
500
  },
501
501
  animationDuration: "300ms",
@@ -508,7 +508,7 @@ const fadeAndSlideInFromRight = StyleSheet.create({
508
508
  animationKeyframes: {
509
509
  from: {
510
510
  opacity: 0,
511
- transform: [{ translateZ: 0 }, { translateX: 10 }],
511
+ transform: "translateZ(0px) translateX(10px)",
512
512
  },
513
513
  },
514
514
  animationDuration: "300ms",
@@ -518,7 +518,7 @@ const fadeAndSlideInFromRight = StyleSheet.create({
518
518
  animationKeyframes: {
519
519
  to: {
520
520
  opacity: 0,
521
- transform: [{ translateZ: 0 }, { translateX: 10 }],
521
+ transform: "translateZ(0px) translateX(10px)",
522
522
  },
523
523
  },
524
524
  animationDuration: "300ms",
@@ -532,16 +532,16 @@ const shake = StyleSheet.create({
532
532
  animationKeyframes: [
533
533
  {
534
534
  "10%, 90%": {
535
- transform: [{ translateX: -1 }],
535
+ transform: "translateX(-1px)",
536
536
  },
537
537
  "20%, 80%": {
538
- transform: [{ translateX: 2 }],
538
+ transform: "translateX(2px)",
539
539
  },
540
540
  "30%, 50%, 70%": {
541
- transform: [{ translateX: -4 }],
541
+ transform: "translateX(-4px)",
542
542
  },
543
543
  "40%, 60%": {
544
- transform: [{ translateX: 4 }],
544
+ transform: "translateX(4px)",
545
545
  },
546
546
  },
547
547
  ],
@@ -1,3 +1,10 @@
1
+ import { useRef } from "react";
1
2
  import { Animated } from "react-native";
2
- import { useLazyRef } from "./useLazyRef";
3
- export const useAnimatedValue = (value) => useLazyRef(() => new Animated.Value(value)).current;
3
+ const UNSET = Symbol("unset");
4
+ export const useAnimatedValue = (value) => {
5
+ const ref = useRef(UNSET);
6
+ if (ref.current === UNSET) {
7
+ ref.current = new Animated.Value(value);
8
+ }
9
+ return ref.current;
10
+ };
@@ -1,4 +1,4 @@
1
- // https://github.com/necolas/react-native-web/blob/0.17.5/packages/react-native-web/src/exports/Pressable/index.js#L220
1
+ // https://github.com/necolas/react-native-web/blob/0.19.1/packages/react-native-web/src/exports/Pressable/index.js#L221
2
2
  import { useState } from "react";
3
3
  export const useForceableState = (forced) => {
4
4
  const [value, setValue] = useState(false);
@@ -1,4 +1,4 @@
1
- // https://github.com/necolas/react-native-web/blob/0.17.5/packages/react-native-web/src/modules/useHover/index.js
1
+ // https://github.com/necolas/react-native-web/blob/0.19.1/packages/react-native-web/src/modules/useHover/index.js
2
2
  // @ts-expect-error
3
3
  import originaUseHover from "react-native-web/dist/cjs/modules/useHover";
4
4
  export const useHover = originaUseHover;
@@ -1,4 +1,4 @@
1
- // https://github.com/necolas/react-native-web/blob/0.17.5/packages/react-native-web/src/modules/useMergeRefs/index.js
1
+ // https://github.com/necolas/react-native-web/blob/0.19.1/packages/react-native-web/src/modules/useMergeRefs/index.js
2
2
  // https://github.com/theKashey/use-callback-ref (for typing)
3
3
  // @ts-expect-error
4
4
  import originalUseMergeRefs from "react-native-web/dist/cjs/modules/useMergeRefs";
@@ -1,4 +1,4 @@
1
- // https://github.com/necolas/react-native-web/blob/0.17.5/packages/react-native-web/src/modules/usePressEvents/index.js
1
+ // https://github.com/necolas/react-native-web/blob/0.19.1/packages/react-native-web/src/modules/usePressEvents/index.js
2
2
  // @ts-expect-error
3
3
  import originalUsePressEvents from "react-native-web/dist/cjs/modules/usePressEvents";
4
4
  export const usePressEvents = originalUsePressEvents;
@@ -1,10 +0,0 @@
1
- /// <reference types="react" />
2
- import { StyleProp, ViewStyle } from "react-native";
3
- type AlertVariant = "info" | "warning" | "error";
4
- type Props = {
5
- variant: AlertVariant;
6
- children: string;
7
- style?: StyleProp<ViewStyle>;
8
- };
9
- export declare const Alert: ({ variant, children, style }: Props) => JSX.Element;
10
- export {};
@@ -1,36 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { StyleSheet, Text } from "react-native";
3
- import { colors } from "../constants/colors";
4
- import { typography } from "../constants/typography";
5
- import { Box } from "./Box";
6
- import { Icon } from "./Icon";
7
- import { Space } from "./Space";
8
- const styles = StyleSheet.create({
9
- base: {
10
- paddingVertical: 12,
11
- paddingHorizontal: 12,
12
- borderRadius: 4,
13
- },
14
- text: {
15
- ...typography.bodySmall,
16
- },
17
- });
18
- const alertIcon = {
19
- info: "info-filled",
20
- warning: "warning-filled",
21
- error: "error-circle-filled",
22
- };
23
- const alertColor = {
24
- info: colors.gray[80],
25
- warning: colors.orange[100],
26
- error: colors.red[100],
27
- };
28
- const alertBackground = {
29
- info: colors.gray[3],
30
- warning: colors.orange[10],
31
- error: colors.red[10],
32
- };
33
- export const Alert = ({ variant, children, style }) => {
34
- const color = alertColor[variant];
35
- return (_jsxs(Box, { direction: "row", alignItems: "center", style: [styles.base, { backgroundColor: alertBackground[variant] }, style], children: [_jsx(Icon, { name: alertIcon[variant], color: color, size: 20 }), _jsx(Space, { width: 12 }), _jsx(Text, { style: [styles.text, { color }], children: children })] }));
36
- };
@@ -1,15 +0,0 @@
1
- import { ReactText } from "react";
2
- import { GestureResponderEvent, StyleProp, View, ViewStyle } from "react-native";
3
- import { IconName } from "./Icon";
4
- type Props = {
5
- children?: ReactText;
6
- color?: string;
7
- disabled?: boolean;
8
- loading?: boolean;
9
- icon?: IconName;
10
- onPress?: (event: GestureResponderEvent) => void;
11
- size?: "large" | "small";
12
- style?: StyleProp<ViewStyle>;
13
- };
14
- export declare const Button: import("react").MemoExoticComponent<import("react").ForwardRefExoticComponent<Props & import("react").RefAttributes<View>>>;
15
- export {};
@@ -1,83 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { forwardRef, memo } from "react";
3
- import { ActivityIndicator, Pressable, StyleSheet, Text, View, } from "react-native";
4
- import { colors } from "../constants/colors";
5
- import { typography } from "../constants/typography";
6
- import { useComputedColors } from "../hooks/useComputedColors";
7
- import { isNotNullish, isNullish } from "../utils/nullish";
8
- import { Icon } from "./Icon";
9
- import { Space } from "./Space";
10
- const styles = StyleSheet.create({
11
- base: {
12
- alignItems: "center",
13
- backgroundColor: colors.black,
14
- borderRadius: 4,
15
- flexDirection: "row",
16
- flexShrink: 1,
17
- height: 48,
18
- justifyContent: "center",
19
- overflow: "hidden",
20
- paddingLeft: 20,
21
- paddingRight: 20,
22
- transitionDuration: "150ms",
23
- transitionProperty: "background-color",
24
- },
25
- small: {
26
- height: 40,
27
- paddingLeft: 16,
28
- paddingRight: 16,
29
- },
30
- withIcon: {
31
- paddingLeft: 18,
32
- },
33
- withIconSmall: {
34
- paddingLeft: 14,
35
- },
36
- iconOnly: {
37
- paddingLeft: 13,
38
- paddingRight: 13,
39
- },
40
- iconOnlySmall: {
41
- paddingLeft: 10,
42
- paddingRight: 10,
43
- },
44
- text: {
45
- ...typography.bodyLarge,
46
- fontWeight: typography.fontWeights.demi,
47
- },
48
- textSmall: {
49
- ...typography.bodySmall,
50
- fontWeight: typography.fontWeights.demi,
51
- },
52
- disabled: {
53
- cursor: "not-allowed",
54
- },
55
- loaderContainer: {
56
- ...StyleSheet.absoluteFillObject,
57
- alignItems: "center",
58
- justifyContent: "center",
59
- },
60
- });
61
- export const Button = memo(forwardRef(({ children, color = colors.gray[100], disabled = false, loading = false, icon, onPress, size = "large", style, }, forwardedRef) => {
62
- const computedColors = useComputedColors(color);
63
- const isSmall = size === "small";
64
- const iconSize = isSmall ? 18 : 20;
65
- return (_jsxs(Pressable, { accessibilityRole: "button", accessibilityBusy: loading, accessibilityDisabled: disabled, disabled: loading || disabled, ref: forwardedRef, onPress: onPress, style: ({ hovered, pressed }) => [
66
- styles.base,
67
- isSmall && styles.small,
68
- isNotNullish(icon) && (isSmall ? styles.withIconSmall : styles.withIcon),
69
- isNullish(children) && (isSmall ? styles.iconOnlySmall : styles.iconOnly),
70
- disabled && styles.disabled,
71
- style,
72
- {
73
- backgroundColor: disabled
74
- ? computedColors.disabled
75
- : pressed
76
- ? computedColors.pressed
77
- : hovered
78
- ? computedColors.hovered
79
- : computedColors.original,
80
- },
81
- ], children: [icon && _jsx(Icon, { color: computedColors.text, name: icon, size: iconSize }), isNotNullish(icon) && isNotNullish(children) && _jsx(Space, { width: isSmall ? 8 : 12 }), _jsx(Text, { numberOfLines: 1, selectable: false, style: [isSmall ? styles.textSmall : styles.text, { color: computedColors.text }], children: children }), loading && (_jsx(View, { accessibilityRole: "none", style: [styles.loaderContainer, { backgroundColor: computedColors.original }], children: _jsx(ActivityIndicator, { color: computedColors.text, size: iconSize }) }))] }));
82
- }));
83
- Button.displayName = "Button";
@@ -1,12 +0,0 @@
1
- import { ReactNode } from "react";
2
- import { StyleProp, View, ViewStyle } from "react-native";
3
- type Props = {
4
- children?: ReactNode;
5
- disabled?: boolean;
6
- error?: string;
7
- style?: StyleProp<ViewStyle>;
8
- value: boolean;
9
- onValueChange?: (value: boolean) => void;
10
- };
11
- export declare const CheckBox: import("react").MemoExoticComponent<import("react").ForwardRefExoticComponent<Props & import("react").RefAttributes<View>>>;
12
- export {};
@@ -1,83 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { forwardRef, memo, useId, useRef } from "react";
3
- import { unstable_createElement as createElement, StyleSheet, Text, View, } from "react-native";
4
- import { colors } from "../constants/colors";
5
- import { typography } from "../constants/typography";
6
- import { useNativeProp } from "../hooks/useNativeProp";
7
- import { isNotNullish } from "../utils/nullish";
8
- import { Icon } from "./Icon";
9
- import { Pressable } from "./Pressable";
10
- import { Space } from "./Space";
11
- const styles = StyleSheet.create({
12
- base: {
13
- flexDirection: "row",
14
- flexShrink: 1,
15
- },
16
- box: {
17
- alignItems: "center",
18
- borderColor: colors.gray[10],
19
- borderRadius: 4,
20
- borderWidth: 1,
21
- height: 20,
22
- justifyContent: "center",
23
- padding: 4,
24
- transitionDuration: "150ms",
25
- transitionProperty: "border-color",
26
- width: 20,
27
- },
28
- native: {
29
- ...StyleSheet.absoluteFillObject,
30
- appearance: "none",
31
- borderColor: colors.transparent,
32
- cursor: "inherit",
33
- height: "100%",
34
- margin: 0,
35
- padding: 0,
36
- width: "100%",
37
- },
38
- icon: {
39
- opacity: 0,
40
- animationKeyframes: {
41
- from: { opacity: 0 },
42
- to: { opacity: 1 },
43
- },
44
- animationDuration: "0.25s",
45
- animationFillMode: "forwards",
46
- animationTimingFunction: "ease",
47
- },
48
- hovered: {
49
- borderColor: colors.gray[30],
50
- },
51
- disabled: {
52
- cursor: "not-allowed",
53
- backgroundColor: colors.gray[3],
54
- },
55
- errored: {
56
- borderColor: colors.red[100],
57
- },
58
- label: {
59
- ...typography.bodyLarge,
60
- lineHeight: typography.lineHeights.title,
61
- },
62
- });
63
- export const CheckBox = memo(forwardRef(({ children, disabled = false, error = "", style, value, onValueChange }, forwardedRef) => {
64
- const ref = useRef(null);
65
- const id = useId();
66
- useNativeProp(ref, "for", id);
67
- return (_jsxs(View, { style: [styles.base, style], children: [_jsxs(Pressable, { ref: forwardedRef, accessibilityRole: "checkbox", focusable: false, accessibilityChecked: value, accessibilityDisabled: disabled, style: ({ hovered }) => [
68
- styles.box,
69
- disabled && styles.disabled,
70
- !disabled && error !== "" && styles.errored,
71
- !disabled && error === "" && hovered && styles.hovered,
72
- ], children: [createElement("input", {
73
- checked: value,
74
- disabled,
75
- id,
76
- style: styles.native,
77
- type: "checkbox",
78
- onChange: () => {
79
- !disabled && onValueChange?.(!value);
80
- },
81
- }), value && (_jsx(Icon, { name: "checkmark-filled", color: disabled ? colors.gray[50] : colors.green[100], size: 16, style: styles.icon }))] }), isNotNullish(children) && (_jsxs(_Fragment, { children: [_jsx(Space, { width: 12 }), _jsx(Text, { accessibilityRole: "label", ref: ref, style: styles.label, children: children })] }))] }));
82
- }));
83
- CheckBox.displayName = "CheckBox";
@@ -1,29 +0,0 @@
1
- import { ReactNode } from "react";
2
- import { StyleProp, ViewStyle } from "react-native";
3
- import { IconName } from "./Icon";
4
- type Props<I extends {
5
- value: string;
6
- disabled?: boolean;
7
- }> = {
8
- value: string;
9
- items: I[];
10
- ListFooterComponent?: ReactNode;
11
- onValueChange: (value: string) => void;
12
- onSelectItem: (value: I) => void;
13
- renderItem: (item: I) => ReactNode | null;
14
- size?: "large" | "small";
15
- icon?: IconName;
16
- label?: string;
17
- placeholder?: string;
18
- loading?: boolean;
19
- disabled?: boolean;
20
- error?: string;
21
- style?: StyleProp<ViewStyle>;
22
- };
23
- export declare const Combobox: (<I extends {
24
- value: string;
25
- disabled?: boolean | undefined;
26
- }>(props: Props<I>) => JSX.Element | null) & {
27
- displayName?: string | undefined;
28
- };
29
- export {};
@@ -1,182 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { memo as reactMemo, useCallback, useEffect, useId, useRef, useState, } from "react";
3
- import isEqual from "react-fast-compare";
4
- import { ActivityIndicator, FlatList, Pressable, StyleSheet, Text, View, } from "react-native";
5
- import { match } from "ts-pattern";
6
- import { colors } from "../constants/colors";
7
- import { spacings } from "../constants/design";
8
- import { typography } from "../constants/typography";
9
- import { useDisclosure } from "../hooks/useDisclosure";
10
- import { useResponsive } from "../hooks/useResponsive";
11
- import { isNotNullish, isNullish } from "../utils/nullish";
12
- import { Icon } from "./Icon";
13
- import { Input } from "./Input";
14
- import { LakeTextInput } from "./LakeTextInput";
15
- import { Popover, VIEWPORT_WIDTH_THRESHOLD } from "./Popover";
16
- const ELEMENT_HEIGHT = 72;
17
- const NB_SUGGESTION_DISPLAYED = 3.5;
18
- const styles = StyleSheet.create({
19
- inputWithLeftIcon: {
20
- paddingLeft: 48,
21
- },
22
- icon: {
23
- position: "absolute",
24
- left: 16,
25
- top: 14,
26
- },
27
- iconWithLabel: {
28
- top: 42,
29
- },
30
- list: {
31
- maxHeight: ELEMENT_HEIGHT * NB_SUGGESTION_DISPLAYED,
32
- },
33
- loading: {
34
- height: ELEMENT_HEIGHT * NB_SUGGESTION_DISPLAYED,
35
- left: 16,
36
- alignItems: "center",
37
- justifyContent: "center",
38
- },
39
- item: {
40
- flexShrink: 1,
41
- flexGrow: 1,
42
- height: ELEMENT_HEIGHT,
43
- justifyContent: "center",
44
- paddingHorizontal: 16,
45
- paddingVertical: 0,
46
- transitionProperty: "background-color",
47
- transitionDuration: "200ms",
48
- },
49
- hoveredItem: {
50
- backgroundColor: colors.gray[3],
51
- },
52
- pressedItem: {
53
- backgroundColor: colors.gray[10],
54
- },
55
- itemText: {
56
- ...typography.bodyLarge,
57
- },
58
- mobileInput: {
59
- padding: spacings[16],
60
- },
61
- });
62
- const isReactText = (node) => ["string", "number"].includes(typeof node);
63
- const getItemLayout = (_data, index) => ({
64
- length: ELEMENT_HEIGHT,
65
- offset: ELEMENT_HEIGHT * index,
66
- index,
67
- });
68
- const memo = reactMemo;
69
- export const Combobox = memo(({ value, items, ListFooterComponent, onValueChange, onSelectItem, renderItem, size, icon, label, placeholder, loading = false, disabled = false, error, style, }) => {
70
- const isFirstRender = useRef(true);
71
- const ref = useRef(null);
72
- const mobileInputRef = useRef(null);
73
- const listRef = useRef(null);
74
- const [visible, { open, close }] = useDisclosure(false);
75
- const [focusedIndex, setFocusedIndex] = useState();
76
- const { desktop } = useResponsive(VIEWPORT_WIDTH_THRESHOLD);
77
- const suggestionsId = useId();
78
- const handleFocus = useCallback((event) => {
79
- if (!desktop) {
80
- event.preventDefault();
81
- open();
82
- }
83
- else {
84
- if (items.length > 0) {
85
- open();
86
- }
87
- }
88
- }, [items, open, desktop]);
89
- const handleKeyPress = useCallback((event) => {
90
- match(event.nativeEvent.key)
91
- .with("ArrowDown", () => {
92
- event.preventDefault();
93
- setFocusedIndex(prevIndex => {
94
- const nextIndex = isNotNullish(prevIndex) ? prevIndex + 1 : 0;
95
- const maxIndex = items.length - 1;
96
- return Math.min(nextIndex, maxIndex);
97
- });
98
- })
99
- .with("ArrowUp", () => {
100
- event.preventDefault();
101
- setFocusedIndex(prevIndex => {
102
- const maxIndex = items.length - 1;
103
- const previousIndex = isNotNullish(prevIndex) ? prevIndex - 1 : maxIndex;
104
- return Math.max(previousIndex, 0);
105
- });
106
- })
107
- .with("Escape", () => {
108
- event.preventDefault();
109
- close();
110
- })
111
- .with("Enter", () => {
112
- event.preventDefault();
113
- if (isNullish(focusedIndex)) {
114
- return;
115
- }
116
- const item = items[focusedIndex];
117
- if (item) {
118
- onSelectItem(item);
119
- close();
120
- }
121
- })
122
- .otherwise(() => { });
123
- }, [items, focusedIndex, onSelectItem, close]);
124
- useEffect(() => {
125
- isFirstRender.current = false;
126
- return () => {
127
- isFirstRender.current = true;
128
- };
129
- }, []);
130
- // Auto open/close popover depending on value on items
131
- useEffect(() => {
132
- // avoid to open popover on first render
133
- if (isFirstRender.current) {
134
- return;
135
- }
136
- if (value.length > 0 && items.length > 0) {
137
- open();
138
- }
139
- // we want to run this effect only when items or value changed
140
- }, [items.length, value.length]); // eslint-disable-line react-hooks/exhaustive-deps
141
- // reset focused suggestion when the number of suggestions change
142
- useEffect(() => {
143
- setFocusedIndex(prevFocusedIndex => {
144
- const maxIndex = items.length - 1;
145
- if (isNotNullish(prevFocusedIndex) && prevFocusedIndex > maxIndex) {
146
- return maxIndex;
147
- }
148
- if (items.length === 0) {
149
- return undefined;
150
- }
151
- return prevFocusedIndex;
152
- });
153
- }, [items.length]);
154
- // scroll automatically in suggestion list if user use keyboard navigation
155
- useEffect(() => {
156
- if (isNotNullish(focusedIndex)) {
157
- const position = focusedIndex + 1 - NB_SUGGESTION_DISPLAYED;
158
- // avoid negative value for first indexes
159
- const index = Math.max(0, position);
160
- listRef.current?.scrollToIndex({
161
- animated: true,
162
- index,
163
- });
164
- }
165
- }, [focusedIndex]);
166
- useEffect(() => {
167
- if (visible) {
168
- mobileInputRef.current?.focus();
169
- }
170
- }, [visible]);
171
- return (_jsxs(View, { style: style, children: [_jsx(Input, { ref: ref, accessibilityControls: visible ? suggestionsId : "", accessibilityExpanded: visible, inputStyle: icon || loading ? styles.inputWithLeftIcon : undefined, returnKeyType: "search", role: "combobox", label: label, placeholder: placeholder, value: value, size: size, disabled: disabled, error: error, onValueChange: onValueChange, onFocus: handleFocus, onKeyPress: handleKeyPress }), icon ? (_jsx(Icon, { name: icon, color: colors.gray[50], size: 20, style: [styles.icon, isNotNullish(label) && styles.iconWithLabel] })) : null, _jsxs(Popover, { id: suggestionsId, role: "listbox", matchReferenceWidth: true, onDismiss: close, referenceRef: ref, autoFocus: false, returnFocus: false, visible: visible && (!desktop || loading || items.length > 0), underlay: false, children: [!desktop && (_jsx(View, { style: styles.mobileInput, children: _jsx(LakeTextInput, { ref: mobileInputRef, accessibilityControls: visible ? suggestionsId : "", accessibilityExpanded: visible, returnKeyType: "search", placeholder: placeholder, value: value, disabled: disabled, error: error, onChangeText: onValueChange, onKeyPress: handleKeyPress }) })), _jsxs(View, { style: styles.list, children: [loading ? (_jsx(View, { style: styles.loading, children: _jsx(ActivityIndicator, { color: colors.gray[70], size: 20 }) })) : (_jsx(FlatList, { ref: listRef, keyExtractor: item => item.value, getItemLayout: getItemLayout, accessibilityRole: "list", data: items, extraData: focusedIndex, renderItem: ({ item, index }) => {
172
- const rendered = renderItem(item);
173
- return (_jsx(Pressable, { accessibilityRole: "listitem", disabled: item.disabled, style: ({ hovered, pressed, focused }) => [
174
- styles.item,
175
- (hovered || focused || index === focusedIndex) && styles.hoveredItem,
176
- pressed && styles.pressedItem,
177
- ], onPress: () => {
178
- onSelectItem(item);
179
- close();
180
- }, children: isReactText(rendered) ? (_jsx(Text, { numberOfLines: 1, selectable: false, style: styles.itemText, children: rendered })) : (rendered) }));
181
- } })), ListFooterComponent] })] })] }));
182
- }, isEqual);
@@ -1,15 +0,0 @@
1
- /// <reference types="react" />
2
- import { NativeSyntheticEvent, TextInput, TextInputProps } from "react-native";
3
- type Props = {
4
- disabled?: boolean;
5
- error?: string;
6
- label: string;
7
- onBlur?: (event: NativeSyntheticEvent<React.FocusEvent>) => void;
8
- onSubmitEditing?: TextInputProps["onSubmitEditing"];
9
- onValueChange?: TextInputProps["onChangeText"];
10
- placeholder?: string;
11
- value?: string;
12
- readOnly?: boolean;
13
- };
14
- export declare const MultilineInput: import("react").MemoExoticComponent<import("react").ForwardRefExoticComponent<Props & import("react").RefAttributes<TextInput>>>;
15
- export {};