@bigbinary/neetoui-rn 3.0.3 → 3.1.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.
- package/README.md +29 -1
- package/lib/typescript/src/components/BottomSheet/BottomSheet.d.ts +8 -1
- package/lib/typescript/src/components/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/lib/typescript/src/components/ChatInput/AttachmentsView.d.ts.map +1 -1
- package/lib/typescript/src/components/ChatInput/ChatInput.d.ts.map +1 -1
- package/lib/typescript/src/components/CheckBox/CheckBox.d.ts +7 -1
- package/lib/typescript/src/components/CheckBox/CheckBox.d.ts.map +1 -1
- package/lib/typescript/src/components/Input/Input.d.ts +12 -11
- package/lib/typescript/src/components/Input/Input.d.ts.map +1 -1
- package/lib/typescript/src/components/Input/InputFrame.d.ts +41 -0
- package/lib/typescript/src/components/Input/InputFrame.d.ts.map +1 -0
- package/lib/typescript/src/components/Input/index.d.ts +1 -0
- package/lib/typescript/src/components/Input/index.d.ts.map +1 -1
- package/lib/typescript/src/components/NeetoUIProvider/NeetoUIProvider.d.ts.map +1 -1
- package/lib/typescript/src/components/RadioButton/RadioButton.d.ts +6 -1
- package/lib/typescript/src/components/RadioButton/RadioButton.d.ts.map +1 -1
- package/lib/typescript/src/components/SearchBar/SearchBar.d.ts.map +1 -1
- package/lib/typescript/src/components/Skeleton/Skeleton.d.ts.map +1 -1
- package/lib/typescript/src/components/Typography/Typography.d.ts +5 -0
- package/lib/typescript/src/components/Typography/Typography.d.ts.map +1 -1
- package/lib/typescript/src/components/Typography/index.d.ts +1 -1
- package/lib/typescript/src/components/Typography/index.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/unistyles.d.ts +28 -1
- package/lib/typescript/src/unistyles.d.ts.map +1 -1
- package/lib/typescript/src/utils/scale.d.ts +22 -0
- package/lib/typescript/src/utils/scale.d.ts.map +1 -1
- package/package.json +8 -6
- package/src/components/Alert/Alert.tsx +57 -46
- package/src/components/BottomSheet/BottomSheet.tsx +98 -55
- package/src/components/BottomSheetMenu/BottomSheetMenu.tsx +55 -48
- package/src/components/ChatInput/AttachmentsView.tsx +32 -18
- package/src/components/ChatInput/ChatInput.tsx +148 -99
- package/src/components/ChatInput/IconButton.tsx +21 -10
- package/src/components/ChatInput/MentionsInput.tsx +42 -27
- package/src/components/CheckBox/CheckBox.tsx +54 -33
- package/src/components/EmptyView/EmptyView.tsx +27 -20
- package/src/components/Header/Header.tsx +25 -18
- package/src/components/Header/ScreenHeader.tsx +17 -10
- package/src/components/Input/Input.tsx +118 -67
- package/src/components/Input/InputFrame.tsx +261 -0
- package/src/components/Input/index.ts +1 -0
- package/src/components/NeetoUIProvider/NeetoUIProvider.tsx +2 -1
- package/src/components/OnBoarding/OnBoarding.tsx +61 -54
- package/src/components/RadioButton/RadioButton.tsx +72 -31
- package/src/components/SearchBar/SearchBar.tsx +41 -26
- package/src/components/Skeleton/Skeleton.tsx +18 -11
- package/src/components/Toast/Toast.tsx +38 -31
- package/src/components/Typography/Typography.tsx +31 -2
- package/src/components/Typography/index.ts +5 -1
- package/src/index.ts +7 -2
- package/src/unistyles-init.ts +4 -0
- package/src/unistyles.ts +52 -4
- package/src/utils/scale.ts +36 -9
- package/src/types/neeto-icons-rn.d.ts +0 -33
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
type TextStyle,
|
|
18
18
|
type ViewStyle,
|
|
19
19
|
} from "react-native";
|
|
20
|
-
import
|
|
20
|
+
import Check from "@bigbinary/neeto-icons-rn/icons/Check";
|
|
21
21
|
import {
|
|
22
22
|
BottomSheetBackdrop,
|
|
23
23
|
BottomSheetFlatList,
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
29
29
|
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
|
30
30
|
|
|
31
|
-
import { moderateScale } from "../../utils/scale";
|
|
31
|
+
import { moderateScale, scaleFor } from "../../utils/scale";
|
|
32
32
|
import { Divider } from "../Divider";
|
|
33
33
|
import { Loader } from "../Loader";
|
|
34
34
|
import { SearchBar, type SearchBarProps } from "../SearchBar";
|
|
@@ -78,7 +78,14 @@ export type BottomSheetProps<T> = {
|
|
|
78
78
|
noResultsLabel?: string;
|
|
79
79
|
NoResultsComponent?: ComponentType;
|
|
80
80
|
onBackdropPress?: () => void;
|
|
81
|
+
/** Absolute cap, in points. Wins over maxHeightRatio. */
|
|
81
82
|
maxHeight?: number;
|
|
83
|
+
/**
|
|
84
|
+
* Cap as a fraction of screen height, so a long list stops short of
|
|
85
|
+
* covering the screen. Override per sheet, or pass maxHeight for an
|
|
86
|
+
* absolute value.
|
|
87
|
+
*/
|
|
88
|
+
maxHeightRatio?: number;
|
|
82
89
|
style?: StyleProp<ViewStyle>;
|
|
83
90
|
titleContainerStyle?: StyleProp<ViewStyle>;
|
|
84
91
|
titleTextStyle?: StyleProp<TextStyle>;
|
|
@@ -117,6 +124,9 @@ const DefaultContentRow = ({
|
|
|
117
124
|
|
|
118
125
|
const ItemSeparator = () => <Divider style={styles.separator} />;
|
|
119
126
|
|
|
127
|
+
/** 60% of the screen: enough to browse, never a full-screen takeover. */
|
|
128
|
+
const DEFAULT_MAX_HEIGHT_RATIO = 0.6;
|
|
129
|
+
|
|
120
130
|
export const BottomSheet = <T,>({
|
|
121
131
|
isVisible,
|
|
122
132
|
hide,
|
|
@@ -144,28 +154,33 @@ export const BottomSheet = <T,>({
|
|
|
144
154
|
NoResultsComponent,
|
|
145
155
|
onBackdropPress,
|
|
146
156
|
maxHeight,
|
|
157
|
+
maxHeightRatio = DEFAULT_MAX_HEIGHT_RATIO,
|
|
147
158
|
style,
|
|
148
159
|
titleContainerStyle,
|
|
149
160
|
titleTextStyle,
|
|
150
161
|
flatListRef,
|
|
151
162
|
sheetProps,
|
|
152
163
|
}: BottomSheetProps<T>) => {
|
|
153
|
-
const { theme } = useUnistyles();
|
|
164
|
+
const { theme, rt } = useUnistyles();
|
|
154
165
|
const insets = useSafeAreaInsets();
|
|
155
166
|
const sheetRef = useRef<BottomSheetModal>(null);
|
|
156
167
|
const [searchText, setSearchText] = useState("");
|
|
157
168
|
|
|
158
169
|
const isVisibleRef = useRef(isVisible);
|
|
159
170
|
isVisibleRef.current = isVisible;
|
|
160
|
-
|
|
171
|
+
// Tracks whether the underlying modal is still presented, so the effect
|
|
172
|
+
// below never dismisses a sheet gorhom has already torn down.
|
|
173
|
+
const isPresentedRef = useRef(false);
|
|
161
174
|
|
|
162
175
|
useEffect(() => {
|
|
163
176
|
if (isVisible) {
|
|
164
|
-
|
|
177
|
+
isPresentedRef.current = true;
|
|
165
178
|
sheetRef.current?.present();
|
|
166
|
-
} else if (
|
|
167
|
-
// Never dismiss() a modal that
|
|
168
|
-
// DISMISSING,
|
|
179
|
+
} else if (isPresentedRef.current) {
|
|
180
|
+
// Never dismiss() a modal that is not presented: gorhom leaves its
|
|
181
|
+
// status at DISMISSING, and from then on handlePortalRender drops
|
|
182
|
+
// every render, so present() can never bring the sheet back.
|
|
183
|
+
isPresentedRef.current = false;
|
|
169
184
|
sheetRef.current?.dismiss();
|
|
170
185
|
}
|
|
171
186
|
setSearchText("");
|
|
@@ -173,8 +188,29 @@ export const BottomSheet = <T,>({
|
|
|
173
188
|
|
|
174
189
|
// Gesture-driven closes (swipe down, backdrop tap, Android back) end here;
|
|
175
190
|
// hand control back to the owner so its isVisible state stays in sync.
|
|
191
|
+
// rt.screen is 0x0 until unistyles has measured the window, and stays 0x0
|
|
192
|
+
// under its jest mock. Deriving a cap from that yields 0, and a sheet
|
|
193
|
+
// pinned to a 0 snap point opens invisible — so leave the cap off until a
|
|
194
|
+
// real height arrives rather than computing a broken one.
|
|
195
|
+
const cappedHeight =
|
|
196
|
+
maxHeight ??
|
|
197
|
+
(rt.screen.height ? rt.screen.height * maxHeightRatio : undefined);
|
|
198
|
+
|
|
199
|
+
// A searchable sheet is pinned to one height: dynamic sizing re-measures
|
|
200
|
+
// the filtered list on every keystroke, so the sheet shrinks under the
|
|
201
|
+
// user while they type. Everything else stays content-sized and only gets
|
|
202
|
+
// the cap — a four-row menu should not claim 60% of the screen.
|
|
203
|
+
const sizingProps =
|
|
204
|
+
canSearch && cappedHeight
|
|
205
|
+
? { snapPoints: [cappedHeight], enableDynamicSizing: false }
|
|
206
|
+
: { maxDynamicContentSize: cappedHeight };
|
|
207
|
+
|
|
176
208
|
const handleDismiss = useCallback(() => {
|
|
177
209
|
setSearchText("");
|
|
210
|
+
// gorhom has already unmounted the sheet and reset its status by the
|
|
211
|
+
// time this fires, so the isVisible=false render that hide() triggers
|
|
212
|
+
// must not dismiss again.
|
|
213
|
+
isPresentedRef.current = false;
|
|
178
214
|
if (!isVisibleRef.current) return;
|
|
179
215
|
|
|
180
216
|
hide();
|
|
@@ -349,8 +385,8 @@ export const BottomSheet = <T,>({
|
|
|
349
385
|
handleIndicatorStyle={styles.handleIndicator}
|
|
350
386
|
keyboardBehavior="interactive"
|
|
351
387
|
keyboardBlurBehavior="restore"
|
|
352
|
-
maxDynamicContentSize={maxHeight}
|
|
353
388
|
ref={sheetRef}
|
|
389
|
+
{...sizingProps}
|
|
354
390
|
{...sheetProps}
|
|
355
391
|
// After the spread, and composed rather than replaced: this is the
|
|
356
392
|
// only channel through which a gesture close reaches the owner, so an
|
|
@@ -420,49 +456,56 @@ export const BottomSheet = <T,>({
|
|
|
420
456
|
);
|
|
421
457
|
};
|
|
422
458
|
|
|
423
|
-
const styles = StyleSheet.create(theme =>
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
459
|
+
const styles = StyleSheet.create((theme, rt) => {
|
|
460
|
+
// Reading rt.screen registers the dependency that has this sheet
|
|
461
|
+
// recomputed on a window resize, so scaled sizes follow rotation,
|
|
462
|
+
// split-screen and foldables instead of freezing at first render.
|
|
463
|
+
const moderateScale = scaleFor(rt.screen);
|
|
464
|
+
|
|
465
|
+
return {
|
|
466
|
+
sheetBackground: {
|
|
467
|
+
backgroundColor: theme.colors.background.primary,
|
|
468
|
+
borderTopLeftRadius: moderateScale(20),
|
|
469
|
+
borderTopRightRadius: moderateScale(20),
|
|
470
|
+
},
|
|
471
|
+
handleIndicator: {
|
|
472
|
+
backgroundColor: theme.colors.palette.grey400,
|
|
473
|
+
},
|
|
474
|
+
separator: {
|
|
475
|
+
backgroundColor: theme.colors.palette.grey200,
|
|
476
|
+
},
|
|
477
|
+
header: {
|
|
478
|
+
backgroundColor: theme.colors.background.primary,
|
|
479
|
+
gap: moderateScale(16),
|
|
480
|
+
paddingBottom: moderateScale(12),
|
|
481
|
+
},
|
|
482
|
+
titleRow: {
|
|
483
|
+
alignItems: "center",
|
|
484
|
+
flexDirection: "row",
|
|
485
|
+
justifyContent: "space-between",
|
|
486
|
+
},
|
|
487
|
+
doneLabel: {
|
|
488
|
+
color: theme.colors.palette.purple500,
|
|
489
|
+
},
|
|
490
|
+
defaultRow: {
|
|
491
|
+
alignItems: "center",
|
|
492
|
+
flexDirection: "row",
|
|
493
|
+
gap: moderateScale(8),
|
|
494
|
+
justifyContent: "space-between",
|
|
495
|
+
minHeight: moderateScale(44),
|
|
496
|
+
},
|
|
497
|
+
createRow: {
|
|
498
|
+
alignItems: "center",
|
|
499
|
+
height: moderateScale(40),
|
|
500
|
+
justifyContent: "center",
|
|
501
|
+
},
|
|
502
|
+
noResults: {
|
|
503
|
+
alignItems: "center",
|
|
504
|
+
marginBottom: moderateScale(2),
|
|
505
|
+
paddingVertical: moderateScale(2),
|
|
506
|
+
},
|
|
507
|
+
mutedLabel: {
|
|
508
|
+
color: theme.colors.text.secondary,
|
|
509
|
+
},
|
|
510
|
+
};
|
|
511
|
+
});
|
|
@@ -2,7 +2,7 @@ import { type ComponentType, type ReactNode } from "react";
|
|
|
2
2
|
import { Pressable, View } from "react-native";
|
|
3
3
|
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
|
4
4
|
|
|
5
|
-
import { moderateScale } from "../../utils/scale";
|
|
5
|
+
import { moderateScale, scaleFor } from "../../utils/scale";
|
|
6
6
|
import { BottomSheet } from "../BottomSheet";
|
|
7
7
|
import { Typography } from "../Typography";
|
|
8
8
|
|
|
@@ -101,50 +101,57 @@ export const BottomSheetMenu = ({
|
|
|
101
101
|
);
|
|
102
102
|
};
|
|
103
103
|
|
|
104
|
-
const styles = StyleSheet.create(theme =>
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
104
|
+
const styles = StyleSheet.create((theme, rt) => {
|
|
105
|
+
// Reading rt.screen registers the dependency that has this sheet
|
|
106
|
+
// recomputed on a window resize, so scaled sizes follow rotation,
|
|
107
|
+
// split-screen and foldables instead of freezing at first render.
|
|
108
|
+
const moderateScale = scaleFor(rt.screen);
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
sheetContent: {
|
|
112
|
+
paddingHorizontal: 0,
|
|
113
|
+
},
|
|
114
|
+
headerContainer: {
|
|
115
|
+
borderBottomColor: theme.colors.palette.grey200,
|
|
116
|
+
borderBottomWidth: moderateScale(1.5),
|
|
117
|
+
paddingBottom: moderateScale(12),
|
|
118
|
+
paddingHorizontal: moderateScale(16),
|
|
119
|
+
paddingTop: moderateScale(8),
|
|
120
|
+
},
|
|
121
|
+
titleContainer: {
|
|
122
|
+
borderBottomColor: theme.colors.palette.grey200,
|
|
123
|
+
borderBottomWidth: moderateScale(1.5),
|
|
124
|
+
paddingBottom: moderateScale(12),
|
|
125
|
+
paddingHorizontal: moderateScale(16),
|
|
126
|
+
},
|
|
127
|
+
title: {
|
|
128
|
+
alignSelf: "center",
|
|
129
|
+
color: theme.colors.text.muted,
|
|
130
|
+
},
|
|
131
|
+
row: {
|
|
132
|
+
alignItems: "center",
|
|
133
|
+
flexDirection: "row",
|
|
134
|
+
gap: moderateScale(12),
|
|
135
|
+
paddingHorizontal: moderateScale(16),
|
|
136
|
+
paddingVertical: moderateScale(14),
|
|
137
|
+
},
|
|
138
|
+
disabledRow: {
|
|
139
|
+
opacity: 0.5,
|
|
140
|
+
},
|
|
141
|
+
separator: {
|
|
142
|
+
borderBottomColor: theme.colors.palette.grey200,
|
|
143
|
+
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
144
|
+
marginHorizontal: moderateScale(16),
|
|
145
|
+
paddingHorizontal: 0,
|
|
146
|
+
},
|
|
147
|
+
cancelButton: {
|
|
148
|
+
alignItems: "center",
|
|
149
|
+
backgroundColor: theme.colors.palette.grey100,
|
|
150
|
+
borderRadius: theme.radii.lg,
|
|
151
|
+
justifyContent: "center",
|
|
152
|
+
marginHorizontal: moderateScale(16),
|
|
153
|
+
marginTop: moderateScale(16),
|
|
154
|
+
paddingVertical: moderateScale(14),
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
});
|
|
@@ -3,7 +3,7 @@ import { Pressable, View } from "react-native";
|
|
|
3
3
|
import Animated, { FadeInDown, FadeInUp } from "react-native-reanimated";
|
|
4
4
|
import { StyleSheet } from "react-native-unistyles";
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { scaleFor } from "../../utils/scale";
|
|
7
7
|
import { Typography } from "../Typography";
|
|
8
8
|
|
|
9
9
|
const Badge = ({
|
|
@@ -36,7 +36,11 @@ export const AttachmentsView = ({
|
|
|
36
36
|
if (attachmentsCount === 0) return null;
|
|
37
37
|
|
|
38
38
|
return isAttachmentsVisible ? (
|
|
39
|
-
|
|
39
|
+
// Held at its natural height: the cards inside are fixed-size, so a
|
|
40
|
+
// squeeze has to come out of the text area, not out of them.
|
|
41
|
+
<Animated.View entering={FadeInUp} style={styles.expanded}>
|
|
42
|
+
{Attachments}
|
|
43
|
+
</Animated.View>
|
|
40
44
|
) : (
|
|
41
45
|
<Animated.View entering={FadeInDown}>
|
|
42
46
|
<Pressable style={styles.badgeRow} onPress={onExpand}>
|
|
@@ -50,19 +54,29 @@ export const AttachmentsView = ({
|
|
|
50
54
|
);
|
|
51
55
|
};
|
|
52
56
|
|
|
53
|
-
const styles = StyleSheet.create(theme =>
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
57
|
+
const styles = StyleSheet.create((theme, rt) => {
|
|
58
|
+
// Reading rt.screen registers the dependency that has this sheet
|
|
59
|
+
// recomputed on a window resize, so scaled sizes follow rotation,
|
|
60
|
+
// split-screen and foldables instead of freezing at first render.
|
|
61
|
+
const moderateScale = scaleFor(rt.screen);
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
expanded: {
|
|
65
|
+
flexShrink: 0,
|
|
66
|
+
},
|
|
67
|
+
badgeRow: {
|
|
68
|
+
alignItems: "flex-start",
|
|
69
|
+
flexDirection: "row",
|
|
70
|
+
flexWrap: "wrap",
|
|
71
|
+
gap: moderateScale(4),
|
|
72
|
+
},
|
|
73
|
+
badge: {
|
|
74
|
+
backgroundColor: theme.colors.background.oldLace,
|
|
75
|
+
borderRadius: moderateScale(20),
|
|
76
|
+
paddingHorizontal: moderateScale(4),
|
|
77
|
+
},
|
|
78
|
+
badgeOnNote: {
|
|
79
|
+
backgroundColor: theme.colors.background.primary,
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
});
|