@bigbinary/neetoui-rn 3.0.3 → 3.1.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.
Files changed (56) hide show
  1. package/README.md +29 -1
  2. package/lib/typescript/src/components/BottomSheet/BottomSheet.d.ts +8 -1
  3. package/lib/typescript/src/components/BottomSheet/BottomSheet.d.ts.map +1 -1
  4. package/lib/typescript/src/components/ChatInput/AttachmentsView.d.ts.map +1 -1
  5. package/lib/typescript/src/components/ChatInput/ChatInput.d.ts.map +1 -1
  6. package/lib/typescript/src/components/CheckBox/CheckBox.d.ts +7 -1
  7. package/lib/typescript/src/components/CheckBox/CheckBox.d.ts.map +1 -1
  8. package/lib/typescript/src/components/Input/Input.d.ts +12 -11
  9. package/lib/typescript/src/components/Input/Input.d.ts.map +1 -1
  10. package/lib/typescript/src/components/Input/InputFrame.d.ts +41 -0
  11. package/lib/typescript/src/components/Input/InputFrame.d.ts.map +1 -0
  12. package/lib/typescript/src/components/Input/index.d.ts +1 -0
  13. package/lib/typescript/src/components/Input/index.d.ts.map +1 -1
  14. package/lib/typescript/src/components/NeetoUIProvider/NeetoUIProvider.d.ts.map +1 -1
  15. package/lib/typescript/src/components/RadioButton/RadioButton.d.ts +6 -1
  16. package/lib/typescript/src/components/RadioButton/RadioButton.d.ts.map +1 -1
  17. package/lib/typescript/src/components/SearchBar/SearchBar.d.ts.map +1 -1
  18. package/lib/typescript/src/components/Skeleton/Skeleton.d.ts.map +1 -1
  19. package/lib/typescript/src/components/Typography/Typography.d.ts +5 -0
  20. package/lib/typescript/src/components/Typography/Typography.d.ts.map +1 -1
  21. package/lib/typescript/src/components/Typography/index.d.ts +1 -1
  22. package/lib/typescript/src/components/Typography/index.d.ts.map +1 -1
  23. package/lib/typescript/src/index.d.ts +2 -2
  24. package/lib/typescript/src/index.d.ts.map +1 -1
  25. package/lib/typescript/src/unistyles.d.ts +28 -1
  26. package/lib/typescript/src/unistyles.d.ts.map +1 -1
  27. package/lib/typescript/src/utils/scale.d.ts +22 -0
  28. package/lib/typescript/src/utils/scale.d.ts.map +1 -1
  29. package/package.json +8 -6
  30. package/src/components/Alert/Alert.tsx +51 -44
  31. package/src/components/BottomSheet/BottomSheet.tsx +98 -55
  32. package/src/components/BottomSheetMenu/BottomSheetMenu.tsx +55 -48
  33. package/src/components/ChatInput/AttachmentsView.tsx +32 -18
  34. package/src/components/ChatInput/ChatInput.tsx +148 -99
  35. package/src/components/ChatInput/IconButton.tsx +21 -10
  36. package/src/components/ChatInput/MentionsInput.tsx +34 -27
  37. package/src/components/CheckBox/CheckBox.tsx +54 -33
  38. package/src/components/EmptyView/EmptyView.tsx +27 -20
  39. package/src/components/Header/Header.tsx +25 -18
  40. package/src/components/Header/ScreenHeader.tsx +17 -10
  41. package/src/components/Input/Input.tsx +111 -67
  42. package/src/components/Input/InputFrame.tsx +261 -0
  43. package/src/components/Input/index.ts +1 -0
  44. package/src/components/NeetoUIProvider/NeetoUIProvider.tsx +2 -1
  45. package/src/components/OnBoarding/OnBoarding.tsx +61 -54
  46. package/src/components/RadioButton/RadioButton.tsx +72 -31
  47. package/src/components/SearchBar/SearchBar.tsx +34 -26
  48. package/src/components/Skeleton/Skeleton.tsx +18 -11
  49. package/src/components/Toast/Toast.tsx +38 -31
  50. package/src/components/Typography/Typography.tsx +31 -2
  51. package/src/components/Typography/index.ts +5 -1
  52. package/src/index.ts +7 -2
  53. package/src/unistyles-init.ts +4 -0
  54. package/src/unistyles.ts +52 -4
  55. package/src/utils/scale.ts +36 -9
  56. package/src/types/neeto-icons-rn.d.ts +0 -33
@@ -1,8 +1,8 @@
1
- import { Radio, RadioInactive } from "@bigbinary/neeto-icons-rn";
2
- import { type StyleProp, type TextStyle } from "react-native";
1
+ import RadioInactive from "@bigbinary/neeto-icons-rn/icons/RadioInactive";
2
+ import { View, type StyleProp, type TextStyle } 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 { Touchable, type TouchableProps } from "../Touchable";
7
7
  import { Typography } from "../Typography";
8
8
 
@@ -13,8 +13,21 @@ export type RadioButtonProps = Omit<TouchableProps, "onPress"> & {
13
13
  label: string;
14
14
  labelStyle?: StyleProp<TextStyle>;
15
15
  iconSize?: number;
16
+ /**
17
+ * Diameter of the filled dot. Defaults to 37.5% of the icon box, which is
18
+ * half the ring's outer diameter — the Material proportion.
19
+ */
20
+ dotSize?: number;
16
21
  };
17
22
 
23
+ /**
24
+ * The icon set's `Radio` glyph fills a 24 box with a ring of radius 8.4 and
25
+ * a dot of radius 3, so the dot reads as a pinprick at the sizes apps use.
26
+ * Drawing the dot here over the plain `RadioInactive` ring keeps both states
27
+ * on identical ring geometry and makes the fill sizeable.
28
+ */
29
+ const DOT_RATIO = 0.375;
30
+
18
31
  export const RadioButton = ({
19
32
  selected = false,
20
33
  onSelect,
@@ -22,6 +35,7 @@ export const RadioButton = ({
22
35
  label,
23
36
  labelStyle,
24
37
  iconSize,
38
+ dotSize,
25
39
  style,
26
40
  ...rest
27
41
  }: RadioButtonProps) => {
@@ -29,7 +43,8 @@ export const RadioButton = ({
29
43
 
30
44
  styles.useVariants({ selected, disabled });
31
45
 
32
- const Icon = selected ? Radio : RadioInactive;
46
+ const size = iconSize ?? moderateScale(22);
47
+ const dot = dotSize ?? size * DOT_RATIO;
33
48
 
34
49
  return (
35
50
  <Touchable
@@ -43,38 +58,64 @@ export const RadioButton = ({
43
58
  <Typography size="m" style={[styles.label, labelStyle]}>
44
59
  {label}
45
60
  </Typography>
46
- <Icon
47
- color={theme.colors.text.primary}
48
- size={iconSize ?? moderateScale(22)}
49
- />
61
+ <View style={styles.iconWrapper}>
62
+ <RadioInactive color={theme.colors.text.primary} size={size} />
63
+ {selected && (
64
+ <View
65
+ style={[
66
+ styles.dot,
67
+ {
68
+ backgroundColor: theme.colors.text.primary,
69
+ borderRadius: dot / 2,
70
+ height: dot,
71
+ width: dot,
72
+ },
73
+ ]}
74
+ />
75
+ )}
76
+ </View>
50
77
  </Touchable>
51
78
  );
52
79
  };
53
80
 
54
- const styles = StyleSheet.create(theme => ({
55
- container: {
56
- alignItems: "center",
57
- flexDirection: "row",
58
- paddingVertical: moderateScale(8),
59
- },
60
- label: {
61
- flex: 1,
62
- lineHeight: moderateScale(22),
63
- variants: {
64
- selected: {
65
- true: {
66
- color: theme.colors.text.primary,
67
- fontFamily: theme.fonts.medium,
81
+ const styles = StyleSheet.create((theme, rt) => {
82
+ // Reading rt.screen registers the dependency that has this sheet
83
+ // recomputed on a window resize, so scaled sizes follow rotation,
84
+ // split-screen and foldables instead of freezing at first render.
85
+ const moderateScale = scaleFor(rt.screen);
86
+
87
+ return {
88
+ container: {
89
+ alignItems: "center",
90
+ flexDirection: "row",
91
+ paddingVertical: moderateScale(8),
92
+ },
93
+ iconWrapper: {
94
+ alignItems: "center",
95
+ justifyContent: "center",
96
+ },
97
+ dot: {
98
+ position: "absolute",
99
+ },
100
+ label: {
101
+ flex: 1,
102
+ lineHeight: moderateScale(22),
103
+ variants: {
104
+ selected: {
105
+ true: {
106
+ color: theme.colors.text.primary,
107
+ fontFamily: theme.fonts.medium,
108
+ },
109
+ false: {
110
+ color: theme.colors.text.secondary,
111
+ fontFamily: theme.fonts.regular,
112
+ },
68
113
  },
69
- false: {
70
- color: theme.colors.text.secondary,
71
- fontFamily: theme.fonts.regular,
114
+ disabled: {
115
+ true: { color: theme.colors.palette.grey400 },
116
+ false: {},
72
117
  },
73
118
  },
74
- disabled: {
75
- true: { color: theme.colors.palette.grey400 },
76
- false: {},
77
- },
78
119
  },
79
- },
80
- }));
120
+ };
121
+ });
@@ -6,11 +6,12 @@ import {
6
6
  type TextInputProps,
7
7
  type ViewStyle,
8
8
  } from "react-native";
9
- import { Close, Search } from "@bigbinary/neeto-icons-rn";
9
+ import Close from "@bigbinary/neeto-icons-rn/icons/Close";
10
+ import Search from "@bigbinary/neeto-icons-rn/icons/Search";
10
11
  import { StyleSheet, useUnistyles } from "react-native-unistyles";
11
12
 
12
13
  import { useDebounce } from "../../hooks";
13
- import { moderateScale } from "../../utils/scale";
14
+ import { moderateScale, scaleFor } from "../../utils/scale";
14
15
  import { Touchable } from "../Touchable";
15
16
 
16
17
  export type SearchBarProps = {
@@ -96,27 +97,34 @@ export const SearchBar = ({
96
97
  );
97
98
  };
98
99
 
99
- const styles = StyleSheet.create(theme => ({
100
- container: {
101
- alignItems: "center",
102
- borderColor: theme.colors.palette.grey400,
103
- borderRadius: moderateScale(8),
104
- borderWidth: 1,
105
- flexDirection: "row",
106
- gap: moderateScale(8),
107
- height: moderateScale(42),
108
- paddingHorizontal: moderateScale(8),
109
- },
110
- input: {
111
- color: theme.colors.text.primary,
112
- flex: 1,
113
- fontFamily: theme.fonts.regular,
114
- fontSize: moderateScale(16),
115
- padding: 0,
116
- },
117
- cancelButton: {
118
- height: "100%",
119
- justifyContent: "center",
120
- paddingHorizontal: moderateScale(8),
121
- },
122
- }));
100
+ const styles = StyleSheet.create((theme, rt) => {
101
+ // Reading rt.screen registers the dependency that has this sheet
102
+ // recomputed on a window resize, so scaled sizes follow rotation,
103
+ // split-screen and foldables instead of freezing at first render.
104
+ const moderateScale = scaleFor(rt.screen);
105
+
106
+ return {
107
+ container: {
108
+ alignItems: "center",
109
+ borderColor: theme.colors.palette.grey400,
110
+ borderRadius: moderateScale(8),
111
+ borderWidth: 1,
112
+ flexDirection: "row",
113
+ gap: moderateScale(8),
114
+ height: moderateScale(42),
115
+ paddingHorizontal: moderateScale(8),
116
+ },
117
+ input: {
118
+ color: theme.colors.text.primary,
119
+ flex: 1,
120
+ fontFamily: theme.fonts.regular,
121
+ fontSize: moderateScale(16),
122
+ padding: 0,
123
+ },
124
+ cancelButton: {
125
+ height: "100%",
126
+ justifyContent: "center",
127
+ paddingHorizontal: moderateScale(8),
128
+ },
129
+ };
130
+ });
@@ -10,7 +10,7 @@ import Animated, {
10
10
  } from "react-native-reanimated";
11
11
  import { StyleSheet, type UnistylesVariants } from "react-native-unistyles";
12
12
 
13
- import { moderateScale } from "../../utils/scale";
13
+ import { moderateScale, scaleFor } from "../../utils/scale";
14
14
 
15
15
  type SkeletonVariants = UnistylesVariants<typeof styles>;
16
16
 
@@ -107,15 +107,22 @@ const SkeletonGroup = ({
107
107
 
108
108
  Skeleton.Group = SkeletonGroup;
109
109
 
110
- const styles = StyleSheet.create(theme => ({
111
- base: {
112
- backgroundColor: theme.colors.palette.grey200,
113
- variants: {
114
- variant: {
115
- rect: { borderRadius: moderateScale(6) },
116
- line: { borderRadius: moderateScale(4) },
117
- circle: { borderRadius: 9999 },
110
+ const styles = StyleSheet.create((theme, rt) => {
111
+ // Reading rt.screen registers the dependency that has this sheet
112
+ // recomputed on a window resize, so scaled sizes follow rotation,
113
+ // split-screen and foldables instead of freezing at first render.
114
+ const moderateScale = scaleFor(rt.screen);
115
+
116
+ return {
117
+ base: {
118
+ backgroundColor: theme.colors.palette.grey200,
119
+ variants: {
120
+ variant: {
121
+ rect: { borderRadius: moderateScale(6) },
122
+ line: { borderRadius: moderateScale(4) },
123
+ circle: { borderRadius: 9999 },
124
+ },
118
125
  },
119
126
  },
120
- },
121
- }));
127
+ };
128
+ });
@@ -1,6 +1,6 @@
1
1
  import { useCallback, useEffect, useRef } from "react";
2
2
  import { Pressable, View, useWindowDimensions } from "react-native";
3
- import { Close } from "@bigbinary/neeto-icons-rn";
3
+ import Close from "@bigbinary/neeto-icons-rn/icons/Close";
4
4
  import ToastMessage, {
5
5
  type ToastConfig,
6
6
  type ToastConfigParams,
@@ -9,7 +9,7 @@ import ToastMessage, {
9
9
  } from "react-native-toast-message";
10
10
  import { StyleSheet, useUnistyles } from "react-native-unistyles";
11
11
 
12
- import { moderateScale } from "../../utils/scale";
12
+ import { moderateScale, scaleFor } from "../../utils/scale";
13
13
  import { Typography } from "../Typography";
14
14
  import { ToastGlyph } from "./ToastGlyph";
15
15
 
@@ -126,32 +126,39 @@ Toast.show = (params: ToastShowParams) =>
126
126
  ToastMessage.show({ autoHide: false, ...params });
127
127
  Toast.hide = () => ToastMessage.hide();
128
128
 
129
- const styles = StyleSheet.create(theme => ({
130
- card: {
131
- alignItems: "center",
132
- backgroundColor: theme.colors.background.secondary,
133
- borderRadius: moderateScale(16),
134
- flexDirection: "row",
135
- gap: moderateScale(10),
136
- marginHorizontal: moderateScale(10),
137
- minHeight: moderateScale(60),
138
- paddingHorizontal: moderateScale(10),
139
- paddingVertical: moderateScale(8),
140
- ...theme.elevation.high,
141
- },
142
- iconSquare: {
143
- alignItems: "center",
144
- borderRadius: moderateScale(10),
145
- height: moderateScale(42),
146
- justifyContent: "center",
147
- width: moderateScale(42),
148
- },
149
- textBlock: {
150
- flex: 1,
151
- gap: moderateScale(2),
152
- paddingVertical: moderateScale(3),
153
- },
154
- description: {
155
- color: theme.colors.text.secondary,
156
- },
157
- }));
129
+ const styles = StyleSheet.create((theme, rt) => {
130
+ // Reading rt.screen registers the dependency that has this sheet
131
+ // recomputed on a window resize, so scaled sizes follow rotation,
132
+ // split-screen and foldables instead of freezing at first render.
133
+ const moderateScale = scaleFor(rt.screen);
134
+
135
+ return {
136
+ card: {
137
+ alignItems: "center",
138
+ backgroundColor: theme.colors.background.secondary,
139
+ borderRadius: moderateScale(16),
140
+ flexDirection: "row",
141
+ gap: moderateScale(10),
142
+ marginHorizontal: moderateScale(10),
143
+ minHeight: moderateScale(60),
144
+ paddingHorizontal: moderateScale(10),
145
+ paddingVertical: moderateScale(8),
146
+ ...theme.elevation.high,
147
+ },
148
+ iconSquare: {
149
+ alignItems: "center",
150
+ borderRadius: moderateScale(10),
151
+ height: moderateScale(42),
152
+ justifyContent: "center",
153
+ width: moderateScale(42),
154
+ },
155
+ textBlock: {
156
+ flex: 1,
157
+ gap: moderateScale(2),
158
+ paddingVertical: moderateScale(3),
159
+ },
160
+ description: {
161
+ color: theme.colors.text.secondary,
162
+ },
163
+ };
164
+ });
@@ -1,8 +1,20 @@
1
- import { Text, type TextProps } from "react-native";
1
+ import {
2
+ Platform,
3
+ StyleSheet as RNStyleSheet,
4
+ Text,
5
+ type TextProps,
6
+ type TextStyle,
7
+ } from "react-native";
2
8
  import { StyleSheet, type UnistylesVariants } from "react-native-unistyles";
3
9
 
4
10
  type TypographyVariants = UnistylesVariants<typeof styles>;
5
11
 
12
+ /**
13
+ * v2's Android guard multiplier, shared with InputFrame so the floating
14
+ * label and the text it stands in for never disagree on line metrics.
15
+ */
16
+ export const LINE_HEIGHT_RATIO = 1.3;
17
+
6
18
  export type TypographyProps = TextProps &
7
19
  TypographyVariants & {
8
20
  color?: string;
@@ -19,8 +31,25 @@ export const Typography = ({
19
31
  }: TypographyProps) => {
20
32
  styles.useVariants({ variant, size, weight });
21
33
 
34
+ const mergedStyle = [styles.text, !!color && { color }, style];
35
+
36
+ // v2's Typography always rendered Android text with includeFontPadding
37
+ // off, vertical centering, and a 1.3x lineHeight computed from the
38
+ // resolved fontSize. Without that guard, SF Pro's tight metrics clip
39
+ // descenders (g/j/y) on Android 15+ (RN >= 0.78 text rendering).
40
+ let androidGuard: TextStyle | false = false;
41
+ if (Platform.OS === "android") {
42
+ const flat = (RNStyleSheet.flatten(mergedStyle) ?? {}) as TextStyle;
43
+ const fontSize = typeof flat.fontSize === "number" ? flat.fontSize : 14;
44
+ androidGuard = {
45
+ includeFontPadding: false,
46
+ textAlignVertical: "center",
47
+ lineHeight: flat.lineHeight ?? Math.floor(fontSize * LINE_HEIGHT_RATIO),
48
+ };
49
+ }
50
+
22
51
  return (
23
- <Text {...rest} style={[styles.text, !!color && { color }, style]}>
52
+ <Text {...rest} style={[...mergedStyle, androidGuard]}>
24
53
  {children}
25
54
  </Text>
26
55
  );
@@ -1 +1,5 @@
1
- export { Typography, type TypographyProps } from "./Typography";
1
+ export {
2
+ LINE_HEIGHT_RATIO,
3
+ Typography,
4
+ type TypographyProps,
5
+ } from "./Typography";
package/src/index.ts CHANGED
@@ -39,7 +39,12 @@ export {
39
39
  type HeaderProps,
40
40
  type ScreenHeaderProps,
41
41
  } from "./components/Header";
42
- export { Input, type InputProps } from "./components/Input";
42
+ export {
43
+ Input,
44
+ InputFrame,
45
+ type InputProps,
46
+ type InputFrameProps,
47
+ } from "./components/Input";
43
48
  export { Loader, type LoaderProps } from "./components/Loader";
44
49
  export {
45
50
  NeetoUIProvider,
@@ -64,5 +69,5 @@ export {
64
69
  lightTheme,
65
70
  type NeetoTheme,
66
71
  } from "./theme/tokens";
67
- export { configureNeetoUI } from "./unistyles";
72
+ export { configureNeetoUI, type ConfigureNeetoUIOptions } from "./unistyles";
68
73
  export { getShadowStyles, isValidEmail, moderateScale } from "./utils";
@@ -11,6 +11,10 @@
11
11
  //
12
12
  // This file (and only this file) is declared in package.json
13
13
  // "sideEffects" so bundlers never tree-shake the bare import away.
14
+ //
15
+ // Registering with the defaults (light theme, no OS following) is safe even
16
+ // for an app that wants adaptive themes: its own configureNeetoUI({
17
+ // adaptiveThemes: true }) call is honoured whenever it lands.
14
18
  import { configureNeetoUI } from "./unistyles";
15
19
 
16
20
  configureNeetoUI();
package/src/unistyles.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { StyleSheet } from "react-native-unistyles";
1
+ import { StyleSheet, UnistylesRuntime } from "react-native-unistyles";
2
2
 
3
3
  import { breakpoints, darkTheme, lightTheme } from "./theme/tokens";
4
4
 
@@ -14,6 +14,22 @@ declare module "react-native-unistyles" {
14
14
  export interface UnistylesBreakpoints extends AppBreakpoints {}
15
15
  }
16
16
 
17
+ export type ConfigureNeetoUIOptions = {
18
+ /**
19
+ * Follow the device colour scheme, swapping to the dark theme whenever the
20
+ * OS is in dark mode. Off by default.
21
+ *
22
+ * An app that has not designed for dark only half-follows: the surfaces
23
+ * styled through theme tokens go dark while everything painted from a
24
+ * literal colour stays light, which reads as dark text on dark
25
+ * backgrounds, light cards adrift on black, and a dark status bar under a
26
+ * `dark-content` bar style. Following the OS is therefore something an app
27
+ * opts into once its own screens are ready, not something the library
28
+ * decides for it.
29
+ */
30
+ adaptiveThemes?: boolean;
31
+ };
32
+
17
33
  let isConfigured = false;
18
34
 
19
35
  /**
@@ -21,14 +37,46 @@ let isConfigured = false;
21
37
  * library root is imported (src/unistyles-init.ts) because Unistyles
22
38
  * requires configure() before any module-scope StyleSheet.create;
23
39
  * NeetoUIProvider and tests may call it again safely.
40
+ *
41
+ * Apps should also call it from their entry point, through the
42
+ * "@bigbinary/neetoui-rn/unistyles" subpath. Metro's inlineRequires defers a
43
+ * named import until the expression that uses it, so importing
44
+ * NeetoUIProvider does not guarantee the library root — and this side
45
+ * effect — runs before the screen modules a default import pulls in eagerly.
46
+ *
47
+ * That ordering is also why an explicit `options` argument is honoured after
48
+ * the first call: the library root usually wins the race and registers the
49
+ * defaults, so an app asking for `adaptiveThemes` would otherwise be
50
+ * silently ignored. Argument-less calls never change an already-applied
51
+ * mode, which keeps NeetoUIProvider's fallback from clobbering that opt-in.
24
52
  */
25
- export const configureNeetoUI = () => {
26
- if (isConfigured) return;
53
+ export const configureNeetoUI = (options?: ConfigureNeetoUIOptions) => {
54
+ const adaptiveThemes = options?.adaptiveThemes ?? false;
55
+
56
+ if (isConfigured) {
57
+ if (options) applyThemeMode(adaptiveThemes);
58
+
59
+ return;
60
+ }
61
+
27
62
  isConfigured = true;
28
63
 
29
64
  StyleSheet.configure({
30
65
  themes: { light: lightTheme, dark: darkTheme },
31
66
  breakpoints,
32
- settings: { adaptiveThemes: true },
67
+ // Unistyles requires exactly one of the two: adaptive themes, or a
68
+ // starting theme it can resolve on its own.
69
+ settings: adaptiveThemes
70
+ ? { adaptiveThemes: true }
71
+ : { initialTheme: "light" },
33
72
  });
34
73
  };
74
+
75
+ const applyThemeMode = (adaptiveThemes: boolean) => {
76
+ if (UnistylesRuntime.hasAdaptiveThemes === adaptiveThemes) return;
77
+
78
+ UnistylesRuntime.setAdaptiveThemes(adaptiveThemes);
79
+ // Turning adaptive themes off leaves whichever theme the OS last selected
80
+ // in place, so a device already in dark mode would stay dark.
81
+ if (!adaptiveThemes) UnistylesRuntime.setTheme("light");
82
+ };
@@ -1,16 +1,43 @@
1
- import { Dimensions } from "react-native";
1
+ import { UnistylesRuntime } from "react-native-unistyles";
2
2
 
3
3
  const GUIDELINE_BASE_WIDTH = 350;
4
4
 
5
- const getShortDimension = () => {
6
- const { width, height } = Dimensions.get("window");
5
+ /**
6
+ * The short screen edge, so a size scales the same in portrait and landscape.
7
+ * Falls back to the baseline before unistyles has measured (and under the jest
8
+ * mock, whose runtime reports 0x0), leaving sizes untouched.
9
+ */
10
+ const shortDimension = ({ width, height }: { width: number; height: number }) =>
11
+ Math.min(width, height) || GUIDELINE_BASE_WIDTH;
7
12
 
8
- return Math.min(width, height);
9
- };
13
+ /** react-native-size-matters' formula, rounded exactly as v2's did. */
14
+ const scale = (screenSize: number, size: number, factor: number) =>
15
+ Math.round(
16
+ size + ((screenSize / GUIDELINE_BASE_WIDTH) * size - size) * factor
17
+ );
10
18
 
11
- const scale = (size: number) =>
12
- (getShortDimension() / GUIDELINE_BASE_WIDTH) * size;
19
+ /**
20
+ * Scales bound to a given screen. Pass `rt.screen` from a unistyles
21
+ * StyleSheet: reading it is what registers the dependency, so the sheet is
22
+ * recomputed when the window resizes — rotation, split-screen, foldables,
23
+ * iPad Slide Over.
24
+ *
25
+ * const styles = StyleSheet.create((theme, rt) => {
26
+ * const moderateScale = scaleFor(rt.screen);
27
+ *
28
+ * return { card: { padding: moderateScale(16) } };
29
+ * });
30
+ */
31
+ export const scaleFor =
32
+ (screen: { width: number; height: number }) =>
33
+ (size: number, factor = 0.5) =>
34
+ scale(shortDimension(screen), size, factor);
13
35
 
14
- // Same contract as v2's moderateScale (react-native-size-matters formula, rounded output).
36
+ /**
37
+ * Screen-independent entry point, for sizes computed outside a stylesheet —
38
+ * icon props, measured layouts. Reads the runtime on every call, so it is
39
+ * current whenever the component re-renders, but it cannot subscribe a
40
+ * stylesheet to resizes: use scaleFor inside StyleSheet.create.
41
+ */
15
42
  export const moderateScale = (size: number, factor = 0.5) =>
16
- Math.round(size + (scale(size) - size) * factor);
43
+ scale(shortDimension(UnistylesRuntime.screen), size, factor);
@@ -1,33 +0,0 @@
1
- // @bigbinary/neeto-icons-rn ships no TypeScript types (and an exports map
2
- // whose "." entry is an extensionless directory only the metro resolver
3
- // accepts). Minimal ambient typing until the icons package ships its own.
4
- declare module "@bigbinary/neeto-icons-rn" {
5
- import { type ComponentType } from "react";
6
-
7
- export type NeetoIconProps = {
8
- size?: number;
9
- color?: string;
10
- strokeWidth?: number;
11
- };
12
-
13
- export const Attachment: ComponentType<NeetoIconProps>;
14
- export const BookOpen: ComponentType<NeetoIconProps>;
15
- export const CannedResponses: ComponentType<NeetoIconProps>;
16
- export const ChatBubble: ComponentType<NeetoIconProps>;
17
- export const Check: ComponentType<NeetoIconProps>;
18
- export const Delete: ComponentType<NeetoIconProps>;
19
- export const Down: ComponentType<NeetoIconProps>;
20
- export const Edit: ComponentType<NeetoIconProps>;
21
- export const Expand: ComponentType<NeetoIconProps>;
22
- export const Forward: ComponentType<NeetoIconProps>;
23
- export const LeftArrow: ComponentType<NeetoIconProps>;
24
- export const Minimize: ComponentType<NeetoIconProps>;
25
- export const Notes: ComponentType<NeetoIconProps>;
26
- export const Radio: ComponentType<NeetoIconProps>;
27
- export const SendPlane: ComponentType<NeetoIconProps>;
28
- export const RadioInactive: ComponentType<NeetoIconProps>;
29
- export const Checkbox: ComponentType<NeetoIconProps>;
30
- export const CheckboxInactive: ComponentType<NeetoIconProps>;
31
- export const Search: ComponentType<NeetoIconProps>;
32
- export const Close: ComponentType<NeetoIconProps>;
33
- }