@onlynative/components 0.1.0-alpha.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 (87) hide show
  1. package/README.md +99 -0
  2. package/dist/appbar/index.d.ts +71 -0
  3. package/dist/appbar/index.js +952 -0
  4. package/dist/button/index.d.ts +41 -0
  5. package/dist/button/index.js +454 -0
  6. package/dist/card/index.d.ts +31 -0
  7. package/dist/card/index.js +264 -0
  8. package/dist/checkbox/index.d.ts +25 -0
  9. package/dist/checkbox/index.js +291 -0
  10. package/dist/chip/index.d.ts +62 -0
  11. package/dist/chip/index.js +452 -0
  12. package/dist/icon-button/index.d.ts +10 -0
  13. package/dist/icon-button/index.js +575 -0
  14. package/dist/index.d.ts +19 -0
  15. package/dist/index.js +3374 -0
  16. package/dist/layout/index.d.ts +98 -0
  17. package/dist/layout/index.js +282 -0
  18. package/dist/list/index.d.ts +60 -0
  19. package/dist/list/index.js +300 -0
  20. package/dist/radio/index.d.ts +25 -0
  21. package/dist/radio/index.js +250 -0
  22. package/dist/switch/index.d.ts +37 -0
  23. package/dist/switch/index.js +315 -0
  24. package/dist/text-field/index.d.ts +52 -0
  25. package/dist/text-field/index.js +496 -0
  26. package/dist/types-D3hlyvz-.d.ts +51 -0
  27. package/dist/typography/index.d.ts +28 -0
  28. package/dist/typography/index.js +69 -0
  29. package/package.json +166 -0
  30. package/src/appbar/AppBar.tsx +302 -0
  31. package/src/appbar/index.ts +2 -0
  32. package/src/appbar/styles.ts +92 -0
  33. package/src/appbar/types.ts +67 -0
  34. package/src/button/Button.tsx +130 -0
  35. package/src/button/index.ts +2 -0
  36. package/src/button/styles.ts +288 -0
  37. package/src/button/types.ts +42 -0
  38. package/src/card/Card.tsx +69 -0
  39. package/src/card/index.ts +2 -0
  40. package/src/card/styles.ts +151 -0
  41. package/src/card/types.ts +27 -0
  42. package/src/checkbox/Checkbox.tsx +109 -0
  43. package/src/checkbox/index.ts +2 -0
  44. package/src/checkbox/styles.ts +155 -0
  45. package/src/checkbox/types.ts +20 -0
  46. package/src/chip/Chip.tsx +182 -0
  47. package/src/chip/index.ts +2 -0
  48. package/src/chip/styles.ts +240 -0
  49. package/src/chip/types.ts +58 -0
  50. package/src/icon-button/IconButton.tsx +358 -0
  51. package/src/icon-button/index.ts +6 -0
  52. package/src/icon-button/styles.ts +259 -0
  53. package/src/icon-button/types.ts +55 -0
  54. package/src/index.ts +51 -0
  55. package/src/layout/Box.tsx +99 -0
  56. package/src/layout/Column.tsx +16 -0
  57. package/src/layout/Grid.tsx +49 -0
  58. package/src/layout/Layout.tsx +81 -0
  59. package/src/layout/Row.tsx +22 -0
  60. package/src/layout/index.ts +13 -0
  61. package/src/layout/resolveSpacing.ts +11 -0
  62. package/src/layout/types.ts +82 -0
  63. package/src/list/List.tsx +17 -0
  64. package/src/list/ListDivider.tsx +20 -0
  65. package/src/list/ListItem.tsx +128 -0
  66. package/src/list/index.ts +9 -0
  67. package/src/list/styles.ts +132 -0
  68. package/src/list/types.ts +54 -0
  69. package/src/radio/Radio.tsx +103 -0
  70. package/src/radio/index.ts +2 -0
  71. package/src/radio/styles.ts +139 -0
  72. package/src/radio/types.ts +20 -0
  73. package/src/switch/Switch.tsx +118 -0
  74. package/src/switch/index.ts +2 -0
  75. package/src/switch/styles.ts +172 -0
  76. package/src/switch/types.ts +32 -0
  77. package/src/test-utils/render-with-theme.tsx +13 -0
  78. package/src/text-field/TextField.tsx +298 -0
  79. package/src/text-field/index.ts +2 -0
  80. package/src/text-field/styles.ts +240 -0
  81. package/src/text-field/types.ts +49 -0
  82. package/src/typography/Typography.tsx +65 -0
  83. package/src/typography/index.ts +3 -0
  84. package/src/typography/types.ts +17 -0
  85. package/src/utils/color.ts +64 -0
  86. package/src/utils/elevation.ts +33 -0
  87. package/src/utils/rtl.ts +19 -0
package/package.json ADDED
@@ -0,0 +1,166 @@
1
+ {
2
+ "name": "@onlynative/components",
3
+ "version": "0.1.0-alpha.0",
4
+ "description": "Material Design 3 UI components for React Native — Button, Card, Chip, TextField, and more.",
5
+ "private": false,
6
+ "sideEffects": false,
7
+ "main": "dist/index.js",
8
+ "module": "dist/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "react-native": "src/index.ts",
11
+ "source": "src/index.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "react-native": "./src/index.ts",
16
+ "default": "./dist/index.js"
17
+ },
18
+ "./typography": {
19
+ "types": "./dist/typography/index.d.ts",
20
+ "react-native": "./src/typography/index.ts",
21
+ "default": "./dist/typography/index.js"
22
+ },
23
+ "./layout": {
24
+ "types": "./dist/layout/index.d.ts",
25
+ "react-native": "./src/layout/index.ts",
26
+ "default": "./dist/layout/index.js"
27
+ },
28
+ "./button": {
29
+ "types": "./dist/button/index.d.ts",
30
+ "react-native": "./src/button/index.ts",
31
+ "default": "./dist/button/index.js"
32
+ },
33
+ "./icon-button": {
34
+ "types": "./dist/icon-button/index.d.ts",
35
+ "react-native": "./src/icon-button/index.ts",
36
+ "default": "./dist/icon-button/index.js"
37
+ },
38
+ "./appbar": {
39
+ "types": "./dist/appbar/index.d.ts",
40
+ "react-native": "./src/appbar/index.ts",
41
+ "default": "./dist/appbar/index.js"
42
+ },
43
+ "./card": {
44
+ "types": "./dist/card/index.d.ts",
45
+ "react-native": "./src/card/index.ts",
46
+ "default": "./dist/card/index.js"
47
+ },
48
+ "./chip": {
49
+ "types": "./dist/chip/index.d.ts",
50
+ "react-native": "./src/chip/index.ts",
51
+ "default": "./dist/chip/index.js"
52
+ },
53
+ "./checkbox": {
54
+ "types": "./dist/checkbox/index.d.ts",
55
+ "react-native": "./src/checkbox/index.ts",
56
+ "default": "./dist/checkbox/index.js"
57
+ },
58
+ "./radio": {
59
+ "types": "./dist/radio/index.d.ts",
60
+ "react-native": "./src/radio/index.ts",
61
+ "default": "./dist/radio/index.js"
62
+ },
63
+ "./switch": {
64
+ "types": "./dist/switch/index.d.ts",
65
+ "react-native": "./src/switch/index.ts",
66
+ "default": "./dist/switch/index.js"
67
+ },
68
+ "./text-field": {
69
+ "types": "./dist/text-field/index.d.ts",
70
+ "react-native": "./src/text-field/index.ts",
71
+ "default": "./dist/text-field/index.js"
72
+ },
73
+ "./list": {
74
+ "types": "./dist/list/index.d.ts",
75
+ "react-native": "./src/list/index.ts",
76
+ "default": "./dist/list/index.js"
77
+ }
78
+ },
79
+ "typesVersions": {
80
+ "*": {
81
+ "typography": [
82
+ "dist/typography/index.d.ts"
83
+ ],
84
+ "layout": [
85
+ "dist/layout/index.d.ts"
86
+ ],
87
+ "button": [
88
+ "dist/button/index.d.ts"
89
+ ],
90
+ "icon-button": [
91
+ "dist/icon-button/index.d.ts"
92
+ ],
93
+ "appbar": [
94
+ "dist/appbar/index.d.ts"
95
+ ],
96
+ "card": [
97
+ "dist/card/index.d.ts"
98
+ ],
99
+ "chip": [
100
+ "dist/chip/index.d.ts"
101
+ ],
102
+ "checkbox": [
103
+ "dist/checkbox/index.d.ts"
104
+ ],
105
+ "radio": [
106
+ "dist/radio/index.d.ts"
107
+ ],
108
+ "switch": [
109
+ "dist/switch/index.d.ts"
110
+ ],
111
+ "text-field": [
112
+ "dist/text-field/index.d.ts"
113
+ ],
114
+ "list": [
115
+ "dist/list/index.d.ts"
116
+ ]
117
+ }
118
+ },
119
+ "publishConfig": {
120
+ "access": "public"
121
+ },
122
+ "files": [
123
+ "dist",
124
+ "src",
125
+ "!src/__tests__"
126
+ ],
127
+ "license": "MIT",
128
+ "repository": {
129
+ "type": "git",
130
+ "url": "https://github.com/onlynative/ui.git",
131
+ "directory": "packages/components"
132
+ },
133
+ "homepage": "https://github.com/onlynative/ui",
134
+ "keywords": [
135
+ "react-native",
136
+ "material-design-3",
137
+ "ui-components",
138
+ "md3",
139
+ "material-you"
140
+ ],
141
+ "scripts": {
142
+ "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",
143
+ "typecheck": "tsc --noEmit",
144
+ "test": "jest --passWithNoTests"
145
+ },
146
+ "peerDependencies": {
147
+ "@expo/vector-icons": ">=14.0.0",
148
+ "@onlynative/core": ">=0.1.0-alpha.0",
149
+ "react": ">=18.0.0",
150
+ "react-native": ">=0.72.0",
151
+ "react-native-safe-area-context": ">=4.0.0"
152
+ },
153
+ "devDependencies": {
154
+ "@react-native/babel-preset": "^0.81.5",
155
+ "@onlynative/core": "workspace:*",
156
+ "@testing-library/react-native": "^13.3.3",
157
+ "@types/jest": "^29.5.14",
158
+ "@types/react": "^19.0.0",
159
+ "jest": "^29.7.0",
160
+ "react": "19.1.0",
161
+ "react-native": "0.81.5",
162
+ "react-test-renderer": "19.1.0",
163
+ "tsup": "^8.0.0",
164
+ "typescript": "^5.0.0"
165
+ }
166
+ }
@@ -0,0 +1,302 @@
1
+ import { useCallback, useMemo, useState } from 'react'
2
+ import type { ReactNode } from 'react'
3
+ import type { LayoutChangeEvent, StyleProp, ViewStyle } from 'react-native'
4
+ import { Platform, View } from 'react-native'
5
+ import { SafeAreaView } from 'react-native-safe-area-context'
6
+ import { defaultTopAppBarTokens, useTheme } from '@onlynative/core'
7
+
8
+ import { IconButton } from '../icon-button'
9
+ import type { IconButtonProps } from '../icon-button'
10
+ import { Typography } from '../typography'
11
+ import type { TypographyVariant } from '../typography'
12
+ import { selectRTL } from '../utils/rtl'
13
+ import { createStyles } from './styles'
14
+ import type { AppBarProps } from './types'
15
+
16
+ type AppBarSize = 'small' | 'medium' | 'large'
17
+ function getBackIcon(): IconButtonProps['icon'] {
18
+ if (Platform.OS === 'ios') {
19
+ return selectRTL('chevron-left', 'chevron-right')
20
+ }
21
+
22
+ return selectRTL('arrow-left', 'arrow-right')
23
+ }
24
+
25
+ const titleVariantBySize: Record<AppBarSize, TypographyVariant> = {
26
+ small: 'titleLarge',
27
+ medium: 'headlineSmall',
28
+ large: 'headlineMedium',
29
+ }
30
+ const APP_BAR_TITLE_TEXT_PROPS = {
31
+ numberOfLines: 1,
32
+ ellipsizeMode: 'tail',
33
+ accessibilityRole: 'header',
34
+ } as const
35
+
36
+ function resolveSize(variant: AppBarProps['variant']): AppBarSize {
37
+ if (variant === 'medium' || variant === 'large') {
38
+ return variant
39
+ }
40
+
41
+ return 'small'
42
+ }
43
+
44
+ function getSizeStyle(
45
+ styles: ReturnType<typeof createStyles>,
46
+ size: AppBarSize,
47
+ ) {
48
+ if (size === 'large') {
49
+ return styles.largeContainer
50
+ }
51
+
52
+ return styles.mediumContainer
53
+ }
54
+
55
+ function withTopInset(
56
+ enabled: boolean,
57
+ content: ReactNode,
58
+ style: StyleProp<ViewStyle>,
59
+ ) {
60
+ if (enabled) {
61
+ return (
62
+ <SafeAreaView edges={['top']} style={style}>
63
+ {content}
64
+ </SafeAreaView>
65
+ )
66
+ }
67
+
68
+ return <View style={style}>{content}</View>
69
+ }
70
+
71
+ function measureWidth(event: LayoutChangeEvent): number {
72
+ return Math.round(event.nativeEvent.layout.width)
73
+ }
74
+
75
+ export function AppBar({
76
+ title,
77
+ variant = 'small',
78
+ canGoBack = false,
79
+ onBackPress,
80
+ insetTop = false,
81
+ elevated = false,
82
+ leading,
83
+ trailing,
84
+ actions,
85
+ containerColor,
86
+ contentColor,
87
+ titleStyle,
88
+ style,
89
+ }: AppBarProps) {
90
+ const theme = useTheme()
91
+ const topAppBar = theme.topAppBar ?? defaultTopAppBarTokens
92
+ const styles = useMemo(() => createStyles(theme), [theme])
93
+ const [leadingWidth, setLeadingWidth] = useState(0)
94
+ const [actionsWidth, setActionsWidth] = useState(0)
95
+ const titleColorStyle = useMemo(
96
+ () => ({ color: contentColor ?? theme.colors.onSurface }),
97
+ [contentColor, theme.colors.onSurface],
98
+ )
99
+ const size = resolveSize(variant)
100
+ const titleVariant = titleVariantBySize[size]
101
+ const isCenterAligned = variant === 'center-aligned'
102
+ const isExpanded = size !== 'small'
103
+ const titleStartInset =
104
+ topAppBar.horizontalPadding +
105
+ Math.max(topAppBar.titleStartInset, leadingWidth)
106
+ const compactTitleEndInset = topAppBar.horizontalPadding + actionsWidth
107
+ const centeredSideInset =
108
+ topAppBar.horizontalPadding + Math.max(leadingWidth, actionsWidth)
109
+ const expandedTitleInsetStyle = useMemo<ViewStyle>(
110
+ () => ({ paddingStart: titleStartInset }),
111
+ [titleStartInset],
112
+ )
113
+ const overlayTitleInsetStyle = useMemo<ViewStyle>(
114
+ () =>
115
+ isCenterAligned
116
+ ? { start: centeredSideInset, end: centeredSideInset }
117
+ : { start: titleStartInset, end: compactTitleEndInset },
118
+ [centeredSideInset, compactTitleEndInset, isCenterAligned, titleStartInset],
119
+ )
120
+
121
+ const leadingContent = useMemo(() => {
122
+ if (leading) {
123
+ return leading
124
+ }
125
+
126
+ if (!canGoBack) {
127
+ return null
128
+ }
129
+
130
+ return (
131
+ <View style={styles.iconFrame}>
132
+ <IconButton
133
+ icon={getBackIcon()}
134
+ size="medium"
135
+ variant="standard"
136
+ iconColor={contentColor ?? theme.colors.onSurface}
137
+ accessibilityLabel="Go back"
138
+ onPress={onBackPress}
139
+ />
140
+ </View>
141
+ )
142
+ }, [
143
+ canGoBack,
144
+ contentColor,
145
+ leading,
146
+ onBackPress,
147
+ styles.iconFrame,
148
+ theme.colors.onSurface,
149
+ ])
150
+
151
+ const actionsContent = useMemo(() => {
152
+ if (trailing) {
153
+ return trailing
154
+ }
155
+
156
+ if (!actions || actions.length === 0) {
157
+ return null
158
+ }
159
+
160
+ return (
161
+ <View style={styles.actionsRow}>
162
+ {actions.map((action, index) => (
163
+ <View
164
+ key={`${String(action.icon)}-${index}`}
165
+ style={styles.iconFrame}
166
+ >
167
+ <IconButton
168
+ icon={action.icon}
169
+ size="medium"
170
+ variant="standard"
171
+ iconColor={contentColor}
172
+ accessibilityLabel={action.accessibilityLabel}
173
+ onPress={action.onPress}
174
+ disabled={action.disabled}
175
+ />
176
+ </View>
177
+ ))}
178
+ </View>
179
+ )
180
+ }, [actions, contentColor, styles.actionsRow, styles.iconFrame, trailing])
181
+
182
+ const onLeadingLayout = useCallback((event: LayoutChangeEvent) => {
183
+ const nextWidth = measureWidth(event)
184
+
185
+ setLeadingWidth((currentWidth) => {
186
+ if (currentWidth === nextWidth) {
187
+ return currentWidth
188
+ }
189
+
190
+ return nextWidth
191
+ })
192
+ }, [])
193
+
194
+ const onActionsLayout = useCallback((event: LayoutChangeEvent) => {
195
+ const nextWidth = measureWidth(event)
196
+
197
+ setActionsWidth((currentWidth) => {
198
+ if (currentWidth === nextWidth) {
199
+ return currentWidth
200
+ }
201
+
202
+ return nextWidth
203
+ })
204
+ }, [])
205
+
206
+ const topRow = (
207
+ <View style={styles.topRow}>
208
+ <View
209
+ collapsable={false}
210
+ onLayout={onLeadingLayout}
211
+ style={styles.sideSlot}
212
+ >
213
+ {leadingContent}
214
+ </View>
215
+ <View style={styles.topRowSpacer} />
216
+ <View
217
+ collapsable={false}
218
+ onLayout={onActionsLayout}
219
+ style={styles.sideSlot}
220
+ >
221
+ {actionsContent}
222
+ </View>
223
+ </View>
224
+ )
225
+
226
+ const containerOverride = containerColor
227
+ ? ({ backgroundColor: containerColor } as ViewStyle)
228
+ : undefined
229
+ const rootStyle: StyleProp<ViewStyle> = [
230
+ styles.root,
231
+ elevated ? styles.elevatedRoot : undefined,
232
+ containerOverride,
233
+ style,
234
+ ]
235
+ const safeAreaStyle: StyleProp<ViewStyle> = [
236
+ styles.safeArea,
237
+ elevated ? styles.elevatedSafeArea : undefined,
238
+ containerOverride,
239
+ ]
240
+
241
+ if (isExpanded) {
242
+ const content = (
243
+ <View style={[styles.expandedContainer, getSizeStyle(styles, size)]}>
244
+ {topRow}
245
+ <View
246
+ style={[
247
+ styles.expandedTitleContainer,
248
+ size === 'large'
249
+ ? styles.largeTitlePadding
250
+ : styles.mediumTitlePadding,
251
+ expandedTitleInsetStyle,
252
+ ]}
253
+ >
254
+ <Typography
255
+ {...APP_BAR_TITLE_TEXT_PROPS}
256
+ variant={titleVariant}
257
+ style={[
258
+ styles.title,
259
+ titleColorStyle,
260
+ styles.startAlignedTitle,
261
+ titleStyle,
262
+ ]}
263
+ >
264
+ {title}
265
+ </Typography>
266
+ </View>
267
+ </View>
268
+ )
269
+
270
+ return (
271
+ <View style={rootStyle}>
272
+ {withTopInset(insetTop, content, safeAreaStyle)}
273
+ </View>
274
+ )
275
+ }
276
+
277
+ const content = (
278
+ <View style={styles.smallContainer}>
279
+ {topRow}
280
+ <View style={[styles.overlayTitleContainer, overlayTitleInsetStyle]}>
281
+ <Typography
282
+ {...APP_BAR_TITLE_TEXT_PROPS}
283
+ variant={titleVariant}
284
+ style={[
285
+ styles.title,
286
+ titleColorStyle,
287
+ isCenterAligned ? styles.centeredTitle : styles.startAlignedTitle,
288
+ titleStyle,
289
+ ]}
290
+ >
291
+ {title}
292
+ </Typography>
293
+ </View>
294
+ </View>
295
+ )
296
+
297
+ return (
298
+ <View style={rootStyle}>
299
+ {withTopInset(insetTop, content, safeAreaStyle)}
300
+ </View>
301
+ )
302
+ }
@@ -0,0 +1,2 @@
1
+ export { AppBar } from './AppBar'
2
+ export type { AppBarAction, AppBarProps, AppBarVariant } from './types'
@@ -0,0 +1,92 @@
1
+ import { StyleSheet } from 'react-native'
2
+ import { defaultTopAppBarTokens } from '@onlynative/core'
3
+ import type { Theme } from '@onlynative/core'
4
+
5
+ export function createStyles(theme: Theme) {
6
+ const topAppBar = theme.topAppBar ?? defaultTopAppBarTokens
7
+
8
+ return StyleSheet.create({
9
+ root: {
10
+ backgroundColor: theme.colors.surface,
11
+ },
12
+ safeArea: {
13
+ backgroundColor: theme.colors.surface,
14
+ },
15
+ elevatedRoot: {
16
+ backgroundColor: theme.colors.surfaceContainer,
17
+ },
18
+ elevatedSafeArea: {
19
+ backgroundColor: theme.colors.surfaceContainer,
20
+ },
21
+ smallContainer: {
22
+ height: topAppBar.smallContainerHeight,
23
+ position: 'relative',
24
+ },
25
+ mediumContainer: {
26
+ height: topAppBar.mediumContainerHeight,
27
+ },
28
+ largeContainer: {
29
+ height: topAppBar.largeContainerHeight,
30
+ },
31
+ expandedContainer: {
32
+ position: 'relative',
33
+ },
34
+ topRow: {
35
+ height: topAppBar.topRowHeight,
36
+ paddingHorizontal: topAppBar.horizontalPadding,
37
+ flexDirection: 'row',
38
+ alignItems: 'center',
39
+ },
40
+ expandedTitleContainer: {
41
+ flex: 1,
42
+ justifyContent: 'flex-end',
43
+ minWidth: 0,
44
+ paddingEnd: theme.spacing.md,
45
+ pointerEvents: 'none',
46
+ },
47
+ topRowSpacer: {
48
+ flex: 1,
49
+ },
50
+ sideSlot: {
51
+ flexDirection: 'row',
52
+ alignItems: 'center',
53
+ minHeight: topAppBar.sideSlotMinHeight,
54
+ },
55
+ actionsRow: {
56
+ flexDirection: 'row',
57
+ alignItems: 'center',
58
+ },
59
+ iconFrame: {
60
+ width: topAppBar.iconFrameSize,
61
+ height: topAppBar.iconFrameSize,
62
+ alignItems: 'center',
63
+ justifyContent: 'center',
64
+ },
65
+ overlayTitleContainer: {
66
+ position: 'absolute',
67
+ top: 0,
68
+ bottom: 0,
69
+ justifyContent: 'center',
70
+ minWidth: 0,
71
+ pointerEvents: 'none',
72
+ },
73
+ centeredTitle: {
74
+ textAlign: 'center',
75
+ },
76
+ startAlignedTitle: {
77
+ textAlign: 'auto',
78
+ },
79
+ mediumTitlePadding: {
80
+ paddingBottom: topAppBar.mediumTitleBottomPadding,
81
+ },
82
+ largeTitlePadding: {
83
+ paddingBottom: topAppBar.largeTitleBottomPadding,
84
+ },
85
+ title: {
86
+ flexShrink: 1,
87
+ maxWidth: '100%',
88
+ includeFontPadding: false,
89
+ textAlignVertical: 'center',
90
+ },
91
+ })
92
+ }
@@ -0,0 +1,67 @@
1
+ import type { ReactNode } from 'react'
2
+ import type { StyleProp, TextStyle, ViewStyle } from 'react-native'
3
+ import type { IconButtonProps } from '../icon-button'
4
+
5
+ /** Size/layout variant of the AppBar. */
6
+ export type AppBarVariant = 'small' | 'center-aligned' | 'medium' | 'large'
7
+
8
+ /** A single action item rendered in the AppBar trailing slot. */
9
+ export interface AppBarAction {
10
+ /** MaterialCommunityIcons icon name to render. */
11
+ icon: IconButtonProps['icon']
12
+ /** Accessibility label for screen readers (required). */
13
+ accessibilityLabel: string
14
+ /** Called when the action icon is pressed. */
15
+ onPress?: () => void
16
+ /**
17
+ * Disables the action icon.
18
+ * @default false
19
+ */
20
+ disabled?: boolean
21
+ }
22
+
23
+ export interface AppBarProps {
24
+ /** Title text displayed in the bar. */
25
+ title: string
26
+ /**
27
+ * Layout variant.
28
+ * @default 'small'
29
+ */
30
+ variant?: AppBarVariant
31
+ /**
32
+ * When `true`, renders a back button in the leading slot.
33
+ * @default false
34
+ */
35
+ canGoBack?: boolean
36
+ /** Called when the auto-rendered back button is pressed. */
37
+ onBackPress?: () => void
38
+ /**
39
+ * When `true`, wraps the bar in a SafeAreaView that handles the top inset.
40
+ * @default false
41
+ */
42
+ insetTop?: boolean
43
+ /**
44
+ * When `true`, adds shadow/elevation to indicate the bar is scrolled.
45
+ * @default false
46
+ */
47
+ elevated?: boolean
48
+ /** Custom leading content. When provided, overrides `canGoBack`. */
49
+ leading?: ReactNode
50
+ /** Custom trailing content. When provided, overrides `actions`. */
51
+ trailing?: ReactNode
52
+ /** Array of icon-button actions rendered in the trailing slot. */
53
+ actions?: AppBarAction[]
54
+ /**
55
+ * Override the container (background) color.
56
+ * Applied to both normal and elevated states.
57
+ */
58
+ containerColor?: string
59
+ /**
60
+ * Override the content (title and icon) color.
61
+ */
62
+ contentColor?: string
63
+ /** Additional style applied to the title text. */
64
+ titleStyle?: StyleProp<TextStyle>
65
+ /** Custom style applied to the root container. */
66
+ style?: StyleProp<ViewStyle>
67
+ }