@onlynative/components 0.1.0-alpha.2 → 0.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 (42) hide show
  1. package/dist/appbar/index.js +75 -123
  2. package/dist/button/index.js +33 -111
  3. package/dist/card/index.js +20 -89
  4. package/dist/checkbox/index.js +11 -65
  5. package/dist/chip/index.js +33 -110
  6. package/dist/icon-button/index.js +33 -87
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.js +535 -517
  9. package/dist/keyboard-avoiding-wrapper/index.d.ts +36 -0
  10. package/dist/keyboard-avoiding-wrapper/index.js +98 -0
  11. package/dist/list/index.js +5 -50
  12. package/dist/radio/index.js +8 -35
  13. package/dist/switch/index.js +13 -67
  14. package/dist/text-field/index.js +27 -69
  15. package/dist/typography/index.js +9 -0
  16. package/package.json +13 -3
  17. package/src/appbar/AppBar.tsx +1 -1
  18. package/src/button/Button.tsx +4 -1
  19. package/src/button/styles.ts +1 -2
  20. package/src/card/styles.ts +1 -2
  21. package/src/checkbox/Checkbox.tsx +5 -1
  22. package/src/checkbox/styles.ts +1 -1
  23. package/src/chip/Chip.tsx +7 -1
  24. package/src/chip/styles.ts +1 -2
  25. package/src/icon-button/IconButton.tsx +6 -2
  26. package/src/icon-button/styles.ts +1 -1
  27. package/src/index.ts +3 -0
  28. package/src/keyboard-avoiding-wrapper/KeyboardAvoidingWrapper.tsx +69 -0
  29. package/src/keyboard-avoiding-wrapper/index.ts +2 -0
  30. package/src/keyboard-avoiding-wrapper/styles.ts +10 -0
  31. package/src/keyboard-avoiding-wrapper/types.ts +37 -0
  32. package/src/list/styles.ts +1 -1
  33. package/src/radio/styles.ts +1 -1
  34. package/src/switch/Switch.tsx +4 -1
  35. package/src/switch/styles.ts +1 -1
  36. package/src/text-field/TextField.tsx +4 -1
  37. package/src/text-field/styles.ts +1 -2
  38. package/src/typography/Typography.tsx +15 -1
  39. package/src/test-utils/render-with-theme.tsx +0 -13
  40. package/src/utils/color.ts +0 -64
  41. package/src/utils/elevation.ts +0 -33
  42. package/src/utils/rtl.ts +0 -19
@@ -25,6 +25,7 @@ __export(typography_exports, {
25
25
  module.exports = __toCommonJS(typography_exports);
26
26
 
27
27
  // src/typography/Typography.tsx
28
+ var import_react = require("react");
28
29
  var import_react_native = require("react-native");
29
30
  var import_core = require("@onlynative/core");
30
31
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -48,6 +49,13 @@ function Typography({
48
49
  const theme = (0, import_core.useTheme)();
49
50
  const typographyStyle = theme.typography[variant];
50
51
  const resolvedRole = accessibilityRole != null ? accessibilityRole : HEADING_VARIANTS.has(variant) ? "header" : void 0;
52
+ const lineHeightFix = (0, import_react.useMemo)(() => {
53
+ if (!style) return void 0;
54
+ const flat = import_react_native.StyleSheet.flatten(style);
55
+ if (!(flat == null ? void 0 : flat.fontSize) || flat.lineHeight) return void 0;
56
+ const ratio = typographyStyle.lineHeight / typographyStyle.fontSize;
57
+ return { lineHeight: Math.ceil(flat.fontSize * ratio) };
58
+ }, [style, typographyStyle.fontSize, typographyStyle.lineHeight]);
51
59
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
52
60
  Component,
53
61
  {
@@ -57,6 +65,7 @@ function Typography({
57
65
  { color: theme.colors.onSurface },
58
66
  typographyStyle,
59
67
  style,
68
+ lineHeightFix,
60
69
  color != null ? { color } : void 0
61
70
  ],
62
71
  children
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onlynative/components",
3
- "version": "0.1.0-alpha.2",
3
+ "version": "0.1.0",
4
4
  "description": "Material Design 3 UI components for React Native — Button, Card, Chip, TextField, and more.",
5
5
  "private": false,
6
6
  "sideEffects": false,
@@ -74,6 +74,11 @@
74
74
  "types": "./dist/list/index.d.ts",
75
75
  "react-native": "./src/list/index.ts",
76
76
  "default": "./dist/list/index.js"
77
+ },
78
+ "./keyboard-avoiding-wrapper": {
79
+ "types": "./dist/keyboard-avoiding-wrapper/index.d.ts",
80
+ "react-native": "./src/keyboard-avoiding-wrapper/index.ts",
81
+ "default": "./dist/keyboard-avoiding-wrapper/index.js"
77
82
  }
78
83
  },
79
84
  "typesVersions": {
@@ -113,6 +118,9 @@
113
118
  ],
114
119
  "list": [
115
120
  "dist/list/index.d.ts"
121
+ ],
122
+ "keyboard-avoiding-wrapper": [
123
+ "dist/keyboard-avoiding-wrapper/index.d.ts"
116
124
  ]
117
125
  }
118
126
  },
@@ -140,13 +148,14 @@
140
148
  "material-you"
141
149
  ],
142
150
  "scripts": {
143
- "build": "tsup src/index.ts src/typography/index.ts src/layout/index.ts src/button/index.ts src/icon-button/index.ts src/appbar/index.ts src/card/index.ts src/chip/index.ts src/checkbox/index.ts src/radio/index.ts src/switch/index.ts src/text-field/index.ts src/list/index.ts --dts --format cjs --outDir dist --clean",
151
+ "build": "tsup src/index.ts src/typography/index.ts src/layout/index.ts src/button/index.ts src/icon-button/index.ts src/appbar/index.ts src/card/index.ts src/chip/index.ts src/checkbox/index.ts src/radio/index.ts src/switch/index.ts src/text-field/index.ts src/list/index.ts src/keyboard-avoiding-wrapper/index.ts --dts --format cjs --outDir dist --clean",
144
152
  "typecheck": "tsc --noEmit",
145
153
  "test": "jest --passWithNoTests"
146
154
  },
147
155
  "peerDependencies": {
148
156
  "@expo/vector-icons": ">=14.0.0",
149
- "@onlynative/core": ">=0.1.0-alpha.2",
157
+ "@onlynative/core": ">=0.1.0",
158
+ "@onlynative/utils": ">=0.1.0-alpha.3",
150
159
  "react": ">=18.0.0",
151
160
  "react-native": ">=0.72.0",
152
161
  "react-native-safe-area-context": ">=4.0.0"
@@ -159,6 +168,7 @@
159
168
  "devDependencies": {
160
169
  "@react-native/babel-preset": "^0.81.5",
161
170
  "@onlynative/core": "workspace:*",
171
+ "@onlynative/utils": "workspace:*",
162
172
  "@testing-library/react-native": "^13.3.3",
163
173
  "@types/jest": "^29.5.14",
164
174
  "@types/react": "^19.0.0",
@@ -9,7 +9,7 @@ import { IconButton } from '../icon-button'
9
9
  import type { IconButtonProps } from '../icon-button'
10
10
  import { Typography } from '../typography'
11
11
  import type { TypographyVariant } from '../typography'
12
- import { selectRTL } from '../utils/rtl'
12
+ import { selectRTL } from '@onlynative/utils'
13
13
  import { createStyles } from './styles'
14
14
  import type { AppBarProps } from './types'
15
15
 
@@ -1,4 +1,3 @@
1
- import MaterialCommunityIcons from '@expo/vector-icons/MaterialCommunityIcons'
2
1
  import { useMemo } from 'react'
3
2
  import { Platform, Pressable } from 'react-native'
4
3
  import { StyleSheet } from 'react-native'
@@ -6,6 +5,7 @@ import { Text } from 'react-native'
6
5
  import type { StyleProp, ViewStyle } from 'react-native'
7
6
  import { useTheme } from '@onlynative/core'
8
7
 
8
+ import { getMaterialCommunityIcons } from '@onlynative/utils'
9
9
  import { createStyles } from './styles'
10
10
  import type { ButtonProps } from './types'
11
11
 
@@ -75,6 +75,9 @@ export function Button({
75
75
  [theme, variant, hasLeading, hasTrailing, containerColor, contentColor],
76
76
  )
77
77
 
78
+ const MaterialCommunityIcons =
79
+ leadingIcon || trailingIcon ? getMaterialCommunityIcons() : null
80
+
78
81
  const resolvedIconColor = useMemo(() => {
79
82
  const base = StyleSheet.flatten([
80
83
  styles.label,
@@ -2,8 +2,7 @@ import { StyleSheet } from 'react-native'
2
2
  import type { MaterialTheme } from '@onlynative/core'
3
3
 
4
4
  import type { ButtonVariant } from './types'
5
- import { alphaColor, blendColor } from '../utils/color'
6
- import { elevationStyle } from '../utils/elevation'
5
+ import { alphaColor, blendColor, elevationStyle } from '@onlynative/utils'
7
6
 
8
7
  interface VariantColors {
9
8
  backgroundColor: string
@@ -2,8 +2,7 @@ import { StyleSheet } from 'react-native'
2
2
  import type { MaterialTheme } from '@onlynative/core'
3
3
 
4
4
  import type { CardVariant } from './types'
5
- import { alphaColor, blendColor } from '../utils/color'
6
- import { elevationStyle } from '../utils/elevation'
5
+ import { alphaColor, blendColor, elevationStyle } from '@onlynative/utils'
7
6
 
8
7
  interface VariantColors {
9
8
  backgroundColor: string
@@ -1,9 +1,9 @@
1
- import MaterialCommunityIcons from '@expo/vector-icons/MaterialCommunityIcons'
2
1
  import { useMemo } from 'react'
3
2
  import { Platform, Pressable, StyleSheet, View } from 'react-native'
4
3
  import type { StyleProp, ViewStyle } from 'react-native'
5
4
  import { useTheme } from '@onlynative/core'
6
5
 
6
+ import { getMaterialCommunityIcons } from '@onlynative/utils'
7
7
  import { createStyles } from './styles'
8
8
  import type { CheckboxProps } from './types'
9
9
 
@@ -55,6 +55,10 @@ export function Checkbox({
55
55
  const isDisabled = Boolean(disabled)
56
56
  const isChecked = Boolean(value)
57
57
 
58
+ const MaterialCommunityIcons = isChecked
59
+ ? getMaterialCommunityIcons()
60
+ : null
61
+
58
62
  const theme = useTheme()
59
63
  const styles = useMemo(
60
64
  () => createStyles(theme, isChecked, containerColor, contentColor),
@@ -1,7 +1,7 @@
1
1
  import { StyleSheet } from 'react-native'
2
2
  import type { MaterialTheme } from '@onlynative/core'
3
3
 
4
- import { alphaColor, blendColor } from '../utils/color'
4
+ import { alphaColor, blendColor } from '@onlynative/utils'
5
5
 
6
6
  interface BoxColors {
7
7
  backgroundColor: string
package/src/chip/Chip.tsx CHANGED
@@ -1,9 +1,9 @@
1
- import MaterialCommunityIcons from '@expo/vector-icons/MaterialCommunityIcons'
2
1
  import { useMemo } from 'react'
3
2
  import { Platform, Pressable, StyleSheet, Text, View } from 'react-native'
4
3
  import type { StyleProp, ViewStyle } from 'react-native'
5
4
  import { useTheme } from '@onlynative/core'
6
5
 
6
+ import { getMaterialCommunityIcons } from '@onlynative/utils'
7
7
  import { createStyles } from './styles'
8
8
  import type { ChipProps } from './types'
9
9
 
@@ -72,6 +72,12 @@ export function Chip({
72
72
  (variant === 'filter' && isSelected),
73
73
  )
74
74
 
75
+ const needsIcons =
76
+ Boolean(leadingIcon) || (variant === 'filter' && isSelected) || showCloseIcon
77
+ const MaterialCommunityIcons = needsIcons
78
+ ? getMaterialCommunityIcons()
79
+ : null
80
+
75
81
  const theme = useTheme()
76
82
  const styles = useMemo(
77
83
  () =>
@@ -2,8 +2,7 @@ import { StyleSheet } from 'react-native'
2
2
  import type { MaterialTheme } from '@onlynative/core'
3
3
 
4
4
  import type { ChipVariant } from './types'
5
- import { alphaColor, blendColor } from '../utils/color'
6
- import { elevationStyle } from '../utils/elevation'
5
+ import { alphaColor, blendColor, elevationStyle } from '@onlynative/utils'
7
6
 
8
7
  interface VariantColors {
9
8
  backgroundColor: string
@@ -1,16 +1,19 @@
1
- import MaterialCommunityIcons from '@expo/vector-icons/MaterialCommunityIcons'
2
1
  import { useMemo } from 'react'
3
2
  import { Pressable } from 'react-native'
4
3
  import type { StyleProp, ViewStyle } from 'react-native'
5
4
  import { useTheme } from '@onlynative/core'
6
5
 
6
+ import {
7
+ alphaColor,
8
+ blendColor,
9
+ getMaterialCommunityIcons,
10
+ } from '@onlynative/utils'
7
11
  import { createStyles } from './styles'
8
12
  import type {
9
13
  IconButtonProps,
10
14
  IconButtonSize,
11
15
  IconButtonVariant,
12
16
  } from './types'
13
- import { alphaColor, blendColor } from '../utils/color'
14
17
 
15
18
  function getIconColor(
16
19
  variant: IconButtonVariant,
@@ -263,6 +266,7 @@ export function IconButton({
263
266
  accessibilityLabel,
264
267
  ...props
265
268
  }: IconButtonProps) {
269
+ const MaterialCommunityIcons = getMaterialCommunityIcons()
266
270
  const theme = useTheme()
267
271
  const styles = useMemo(() => createStyles(theme), [theme])
268
272
  const isDisabled = Boolean(disabled)
@@ -1,6 +1,6 @@
1
1
  import { StyleSheet } from 'react-native'
2
2
  import type { MaterialTheme } from '@onlynative/core'
3
- import { alphaColor, blendColor } from '../utils/color'
3
+ import { alphaColor, blendColor } from '@onlynative/utils'
4
4
 
5
5
  export function createStyles(theme: MaterialTheme) {
6
6
  const disabledContainerColor = alphaColor(theme.colors.onSurface, 0.12)
package/src/index.ts CHANGED
@@ -49,3 +49,6 @@ export type {
49
49
  ListItemProps,
50
50
  ListDividerProps,
51
51
  } from './list'
52
+
53
+ export { KeyboardAvoidingWrapper } from './keyboard-avoiding-wrapper'
54
+ export type { KeyboardAvoidingWrapperProps } from './keyboard-avoiding-wrapper'
@@ -0,0 +1,69 @@
1
+ import { useEffect } from 'react'
2
+ import {
3
+ Keyboard,
4
+ KeyboardAvoidingView,
5
+ Platform,
6
+ ScrollView,
7
+ } from 'react-native'
8
+
9
+ import type { KeyboardAvoidingWrapperProps } from './types'
10
+ import { styles } from './styles'
11
+
12
+ const isIOS = Platform.OS === 'ios'
13
+
14
+ export function KeyboardAvoidingWrapper({
15
+ children,
16
+ behavior = 'padding',
17
+ keyboardVerticalOffset = 0,
18
+ enabled = true,
19
+ scrollViewProps,
20
+ onKeyboardShow,
21
+ onKeyboardHide,
22
+ style,
23
+ contentContainerStyle,
24
+ }: KeyboardAvoidingWrapperProps) {
25
+ useEffect(() => {
26
+ const subscriptions: ReturnType<typeof Keyboard.addListener>[] = []
27
+
28
+ if (onKeyboardShow) {
29
+ const showEvent = isIOS
30
+ ? 'keyboardWillShow'
31
+ : 'keyboardDidShow'
32
+ subscriptions.push(
33
+ Keyboard.addListener(showEvent, onKeyboardShow),
34
+ )
35
+ }
36
+
37
+ if (onKeyboardHide) {
38
+ const hideEvent = isIOS
39
+ ? 'keyboardWillHide'
40
+ : 'keyboardDidHide'
41
+ subscriptions.push(
42
+ Keyboard.addListener(hideEvent, onKeyboardHide),
43
+ )
44
+ }
45
+
46
+ return () => {
47
+ subscriptions.forEach((sub) => sub.remove())
48
+ }
49
+ }, [onKeyboardShow, onKeyboardHide])
50
+
51
+ return (
52
+ <KeyboardAvoidingView
53
+ style={[styles.root, style]}
54
+ behavior={behavior}
55
+ keyboardVerticalOffset={keyboardVerticalOffset}
56
+ enabled={!isIOS && enabled}
57
+ >
58
+ <ScrollView
59
+ automaticallyAdjustKeyboardInsets={isIOS && enabled}
60
+ keyboardShouldPersistTaps="handled"
61
+ showsVerticalScrollIndicator={false}
62
+ {...scrollViewProps}
63
+ contentContainerStyle={[styles.container, contentContainerStyle]}
64
+ >
65
+ {children}
66
+ </ScrollView>
67
+ </KeyboardAvoidingView>
68
+ )
69
+ }
@@ -0,0 +1,2 @@
1
+ export { KeyboardAvoidingWrapper } from './KeyboardAvoidingWrapper'
2
+ export type { KeyboardAvoidingWrapperProps } from './types'
@@ -0,0 +1,10 @@
1
+ import { StyleSheet } from 'react-native'
2
+
3
+ export const styles = StyleSheet.create({
4
+ root: {
5
+ flex: 1,
6
+ },
7
+ container: {
8
+ flexGrow: 1,
9
+ },
10
+ })
@@ -0,0 +1,37 @@
1
+ import type { PropsWithChildren } from 'react'
2
+ import type {
3
+ KeyboardAvoidingViewProps,
4
+ KeyboardEvent,
5
+ ScrollViewProps,
6
+ StyleProp,
7
+ ViewStyle,
8
+ } from 'react-native'
9
+
10
+ export interface KeyboardAvoidingWrapperProps extends PropsWithChildren {
11
+ /**
12
+ * Keyboard avoidance strategy.
13
+ * @default 'padding'
14
+ */
15
+ behavior?: KeyboardAvoidingViewProps['behavior']
16
+ /**
17
+ * Extra offset added to the keyboard height calculation.
18
+ * Useful for accounting for headers or tab bars.
19
+ * @default 0
20
+ */
21
+ keyboardVerticalOffset?: number
22
+ /**
23
+ * Enable or disable the keyboard avoiding behavior.
24
+ * @default true
25
+ */
26
+ enabled?: boolean
27
+ /** Props forwarded to the inner `ScrollView`. */
28
+ scrollViewProps?: ScrollViewProps
29
+ /** Called when the keyboard is about to show (iOS) or has shown (Android). */
30
+ onKeyboardShow?: (event: KeyboardEvent) => void
31
+ /** Called when the keyboard is about to hide (iOS) or has hidden (Android). */
32
+ onKeyboardHide?: (event: KeyboardEvent) => void
33
+ /** Style applied to the outer `KeyboardAvoidingView`. */
34
+ style?: StyleProp<ViewStyle>
35
+ /** Style applied to the inner `ScrollView` contentContainerStyle. */
36
+ contentContainerStyle?: StyleProp<ViewStyle>
37
+ }
@@ -2,7 +2,7 @@ import { StyleSheet } from 'react-native'
2
2
  import type { MaterialTheme } from '@onlynative/core'
3
3
 
4
4
  import type { ListItemLines } from './types'
5
- import { alphaColor, blendColor } from '../utils/color'
5
+ import { alphaColor, blendColor } from '@onlynative/utils'
6
6
 
7
7
  const ITEM_PADDING_VERTICAL = 12
8
8
  const INSET_START = 56
@@ -1,7 +1,7 @@
1
1
  import { StyleSheet } from 'react-native'
2
2
  import type { MaterialTheme } from '@onlynative/core'
3
3
 
4
- import { alphaColor } from '../utils/color'
4
+ import { alphaColor } from '@onlynative/utils'
5
5
 
6
6
  interface RadioColors {
7
7
  borderColor: string
@@ -1,9 +1,9 @@
1
- import MaterialCommunityIcons from '@expo/vector-icons/MaterialCommunityIcons'
2
1
  import { useMemo } from 'react'
3
2
  import { Platform, Pressable, StyleSheet, View } from 'react-native'
4
3
  import type { StyleProp, ViewStyle } from 'react-native'
5
4
  import { useTheme } from '@onlynative/core'
6
5
 
6
+ import { getMaterialCommunityIcons } from '@onlynative/utils'
7
7
  import { createStyles } from './styles'
8
8
  import type { SwitchProps } from './types'
9
9
 
@@ -80,6 +80,9 @@ export function Switch({
80
80
  }
81
81
 
82
82
  const iconName = isSelected ? selectedIcon : unselectedIcon
83
+ const MaterialCommunityIcons = iconName
84
+ ? getMaterialCommunityIcons()
85
+ : null
83
86
  const iconSize = 16
84
87
 
85
88
  return (
@@ -1,7 +1,7 @@
1
1
  import { StyleSheet } from 'react-native'
2
2
  import type { MaterialTheme } from '@onlynative/core'
3
3
 
4
- import { alphaColor, blendColor } from '../utils/color'
4
+ import { alphaColor, blendColor } from '@onlynative/utils'
5
5
 
6
6
  interface TrackColors {
7
7
  trackColor: string
@@ -1,4 +1,3 @@
1
- import MaterialCommunityIcons from '@expo/vector-icons/MaterialCommunityIcons'
2
1
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
3
2
  import {
4
3
  Animated,
@@ -11,6 +10,7 @@ import {
11
10
  import type { NativeSyntheticEvent, TargetedEvent } from 'react-native'
12
11
  import { useTheme } from '@onlynative/core'
13
12
 
13
+ import { getMaterialCommunityIcons } from '@onlynative/utils'
14
14
  import { createStyles, labelPositions } from './styles'
15
15
  import type { TextFieldProps } from './types'
16
16
 
@@ -46,6 +46,9 @@ export function TextField({
46
46
  const isFilled = variant === 'filled'
47
47
  const hasLeadingIcon = Boolean(leadingIcon)
48
48
 
49
+ const MaterialCommunityIcons =
50
+ leadingIcon || trailingIcon ? getMaterialCommunityIcons() : null
51
+
49
52
  const { colors, styles } = useMemo(
50
53
  () => createStyles(theme, variant),
51
54
  [theme, variant],
@@ -2,8 +2,7 @@ import { StyleSheet } from 'react-native'
2
2
  import type { MaterialTheme } from '@onlynative/core'
3
3
 
4
4
  import type { TextFieldVariant } from './types'
5
- import { alphaColor } from '../utils/color'
6
- import { transformOrigin } from '../utils/rtl'
5
+ import { alphaColor, transformOrigin } from '@onlynative/utils'
7
6
 
8
7
  const CONTAINER_HEIGHT = 56
9
8
  const ICON_SIZE = 24
@@ -1,6 +1,7 @@
1
+ import { useMemo } from 'react'
1
2
  import type { ComponentType, ReactNode } from 'react'
2
3
  import type { StyleProp, TextProps, TextStyle } from 'react-native'
3
- import { Text } from 'react-native'
4
+ import { StyleSheet, Text } from 'react-native'
4
5
  import { useTheme } from '@onlynative/core'
5
6
  import type { MaterialTheme } from '@onlynative/core'
6
7
 
@@ -48,6 +49,18 @@ export function Typography({
48
49
  const resolvedRole =
49
50
  accessibilityRole ?? (HEADING_VARIANTS.has(variant) ? 'header' : undefined)
50
51
 
52
+ // When the consumer overrides fontSize via style, auto-adjust lineHeight
53
+ // proportionally so text isn't clipped inside overflow:hidden containers.
54
+ // Skipped when: no style prop (theme lineHeight is already proportional),
55
+ // no fontSize override, or consumer explicitly sets lineHeight.
56
+ const lineHeightFix = useMemo(() => {
57
+ if (!style) return undefined
58
+ const flat = StyleSheet.flatten(style)
59
+ if (!flat?.fontSize || flat.lineHeight) return undefined
60
+ const ratio = typographyStyle.lineHeight / typographyStyle.fontSize
61
+ return { lineHeight: Math.ceil(flat.fontSize * ratio) }
62
+ }, [style, typographyStyle.fontSize, typographyStyle.lineHeight])
63
+
51
64
  return (
52
65
  <Component
53
66
  {...textProps}
@@ -56,6 +69,7 @@ export function Typography({
56
69
  { color: theme.colors.onSurface },
57
70
  typographyStyle,
58
71
  style,
72
+ lineHeightFix,
59
73
  color != null ? { color } : undefined,
60
74
  ]}
61
75
  >
@@ -1,13 +0,0 @@
1
- import { render, type RenderOptions } from '@testing-library/react-native'
2
- import type { ReactElement } from 'react'
3
- import { ThemeProvider } from '@onlynative/core'
4
-
5
- export function renderWithTheme(
6
- ui: ReactElement,
7
- options?: Omit<RenderOptions, 'wrapper'>,
8
- ) {
9
- return render(ui, {
10
- wrapper: ({ children }) => <ThemeProvider>{children}</ThemeProvider>,
11
- ...options,
12
- })
13
- }
@@ -1,64 +0,0 @@
1
- interface RgbChannels {
2
- r: number
3
- g: number
4
- b: number
5
- }
6
-
7
- function parseHexColor(color: string): RgbChannels | null {
8
- const normalized = color.replace('#', '')
9
-
10
- if (normalized.length !== 6 && normalized.length !== 8) {
11
- return null
12
- }
13
-
14
- const r = Number.parseInt(normalized.slice(0, 2), 16)
15
- const g = Number.parseInt(normalized.slice(2, 4), 16)
16
- const b = Number.parseInt(normalized.slice(4, 6), 16)
17
-
18
- if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) {
19
- return null
20
- }
21
-
22
- return { r, g, b }
23
- }
24
-
25
- function clampAlpha(alpha: number): number {
26
- return Math.max(0, Math.min(1, alpha))
27
- }
28
-
29
- export function alphaColor(color: string, alpha: number): string {
30
- const channels = parseHexColor(color)
31
- const boundedAlpha = clampAlpha(alpha)
32
-
33
- if (!channels) {
34
- return color
35
- }
36
-
37
- return `rgba(${channels.r}, ${channels.g}, ${channels.b}, ${boundedAlpha})`
38
- }
39
-
40
- export function blendColor(
41
- base: string,
42
- overlay: string,
43
- overlayAlpha: number,
44
- ): string {
45
- const baseChannels = parseHexColor(base)
46
- const overlayChannels = parseHexColor(overlay)
47
- const boundedAlpha = clampAlpha(overlayAlpha)
48
-
49
- if (!baseChannels || !overlayChannels) {
50
- return alphaColor(overlay, boundedAlpha)
51
- }
52
-
53
- const r = Math.round(
54
- (1 - boundedAlpha) * baseChannels.r + boundedAlpha * overlayChannels.r,
55
- )
56
- const g = Math.round(
57
- (1 - boundedAlpha) * baseChannels.g + boundedAlpha * overlayChannels.g,
58
- )
59
- const b = Math.round(
60
- (1 - boundedAlpha) * baseChannels.b + boundedAlpha * overlayChannels.b,
61
- )
62
-
63
- return `rgb(${r}, ${g}, ${b})`
64
- }
@@ -1,33 +0,0 @@
1
- import { Platform } from 'react-native'
2
- import type { ViewStyle } from 'react-native'
3
- import type { ElevationLevel } from '@onlynative/core'
4
-
5
- /**
6
- * Converts a theme elevation level into platform-appropriate shadow styles.
7
- * - Native: uses shadow* props + elevation
8
- * - Web: uses boxShadow string
9
- */
10
- export function elevationStyle(level: ElevationLevel): ViewStyle {
11
- if (Platform.OS === 'web') {
12
- const { shadowOffset, shadowOpacity, shadowRadius } = level
13
-
14
- if (shadowOpacity === 0) {
15
- return { boxShadow: 'none' } as ViewStyle
16
- }
17
-
18
- return {
19
- boxShadow: `${shadowOffset.width}px ${shadowOffset.height}px ${shadowRadius}px rgba(0, 0, 0, ${shadowOpacity})`,
20
- } as ViewStyle
21
- }
22
-
23
- return {
24
- shadowColor: level.shadowColor,
25
- shadowOffset: {
26
- width: level.shadowOffset.width,
27
- height: level.shadowOffset.height,
28
- },
29
- shadowOpacity: level.shadowOpacity,
30
- shadowRadius: level.shadowRadius,
31
- elevation: level.elevation,
32
- }
33
- }
package/src/utils/rtl.ts DELETED
@@ -1,19 +0,0 @@
1
- import { I18nManager } from 'react-native'
2
-
3
- /**
4
- * Returns the appropriate transform origin for animations that scale
5
- * from a horizontal edge (e.g. label shrink in TextField).
6
- */
7
- export function transformOrigin(
8
- vertical: 'top' | 'center' | 'bottom' = 'top',
9
- ): string {
10
- return I18nManager.isRTL ? `right ${vertical}` : `left ${vertical}`
11
- }
12
-
13
- /**
14
- * Picks a value based on layout direction.
15
- * Useful for selecting mirrored icons or other direction-dependent values.
16
- */
17
- export function selectRTL<T>(ltr: T, rtl: T): T {
18
- return I18nManager.isRTL ? rtl : ltr
19
- }